Adding RSS feeds for saved tags. Still needs the actual rss feed.

This commit is contained in:
Samuel Clay 2013-12-19 11:08:22 -08:00
parent e368abd7d0
commit feb398a2d4
12 changed files with 213 additions and 42 deletions

View file

@ -19,6 +19,7 @@ urlpatterns = patterns('',
url(r'^feed_unread_count', views.feed_unread_count, name='feed-unread-count'),
url(r'^starred_stories', views.load_starred_stories, name='load-starred-stories'),
url(r'^starred_story_hashes', views.starred_story_hashes, name='starred-story-hashes'),
url(r'^starred_rss/(?P<user_id>\d+)/(?P<secret_token>\w+)/(?P<tag_slug>[-\w]+)?/?$', views.starred_stories_rss_feed, name='load-starred-rss'),
url(r'^unread_story_hashes', views.unread_story_hashes, name='unread-story-hashes'),
url(r'^mark_all_as_read', views.mark_all_as_read, name='mark-all-as-read'),
url(r'^mark_story_as_read', views.mark_story_as_read, name='mark-story-as-read'),

View file

@ -849,6 +849,67 @@ def starred_story_hashes(request):
return dict(starred_story_hashes=story_hashes)
def starred_stories_rss_feed(request, user_id, secret_token, tag_slug):
try:
user = User.objects.get(pk=user_id)
except User.DoesNotExist:
raise Http404
username = username and username.lower()
profile = MSocialProfile.get_user(user.pk)
params = {'username': profile.username_slug, 'user_id': user.pk}
if not username or profile.username_slug.lower() != username:
return HttpResponseRedirect(reverse('shared-stories-rss-feed', kwargs=params))
social_profile = MSocialProfile.get_user(user_id)
current_site = Site.objects.get_current()
current_site = current_site and current_site.domain
if social_profile.private:
return HttpResponseForbidden()
data = {}
data['title'] = social_profile.title
data['link'] = social_profile.blurblog_url
data['description'] = "Stories shared by %s on NewsBlur." % user.username
data['lastBuildDate'] = datetime.datetime.utcnow()
data['generator'] = 'NewsBlur - http://www.newsblur.com'
data['docs'] = None
data['author_name'] = user.username
data['feed_url'] = "http://%s%s" % (
current_site,
reverse('shared-stories-rss-feed', kwargs=params),
)
rss = feedgenerator.Atom1Feed(**data)
shared_stories = MSharedStory.objects.filter(user_id=user.pk).order_by('-shared_date')[:25]
for shared_story in shared_stories:
feed = Feed.get_by_id(shared_story.story_feed_id)
content = render_to_string('social/rss_story.xhtml', {
'feed': feed,
'user': user,
'social_profile': social_profile,
'shared_story': shared_story,
'content': (shared_story.story_content_z and
zlib.decompress(shared_story.story_content_z))
})
story_data = {
'title': shared_story.story_title,
'link': shared_story.story_permalink,
'description': content,
'author_name': shared_story.story_author_name,
'categories': shared_story.story_tags,
'unique_id': shared_story.story_guid,
'pubdate': shared_story.shared_date,
}
rss.add_item(**story_data)
logging.user(request, "~FBGenerating ~SB%s~SN's RSS feed: ~FM%s" % (
user.username,
request.META.get('HTTP_USER_AGENT', "")[:24]
))
return HttpResponse(rss.writeString('utf-8'), content_type='application/rss+xml')
@json.json_view
def load_river_stories__redis(request):
limit = 12

View file

@ -2062,6 +2062,7 @@ class MStarredStory(mongo.Document):
class MStarredStoryCounts(mongo.Document):
user_id = mongo.IntField()
tag = mongo.StringField(max_length=128, unique_with=['user_id'])
slug = mongo.StringField(max_length=128)
count = mongo.IntField()
meta = {
@ -2070,15 +2071,25 @@ class MStarredStoryCounts(mongo.Document):
'ordering': ['tag'],
'allow_inheritance': False,
}
@property
def rss_url(self, secret_token=None):
if not secret_token:
user = User.objects.select_related('profile').get(pk=self.user_id)
secret_token = user.profile.secret_token
return "%s/reader/starred_rss/%s/%s/%s" % (settings.NEWSBLUR_URL, self.user_id,
secret_token, self.slug)
@classmethod
def user_counts(cls, user_id, include_total=False, try_counting=True):
counts = cls.objects.filter(user_id=user_id).only('tag', 'count')
counts = [{'tag': c.tag, 'count': c.count} for c in counts]
counts = cls.objects.filter(user_id=user_id)
counts = [{'tag': c.tag, 'count': c.count, 'feed_address': c.rss_url} for c in counts]
if counts == [] and try_counting:
cls.count_tags_for_user(user_id)
return cls.user_counts(user_id, include_total=include_total, try_counting=False)
return cls.user_counts(user_id, include_total=include_total,
try_counting=False)
if include_total:
for c in counts:
@ -2098,13 +2109,13 @@ class MStarredStoryCounts(mongo.Document):
cls.objects(user_id=user_id).delete()
for tag, count in dict(user_tags).items():
cls.objects.create(user_id=user_id, tag=tag, count=count)
cls.objects.create(user_id=user_id, tag=tag, slug=slugify(tag), count=count)
total_stories_count = MStarredStory.objects(user_id=user_id).count()
cls.objects.create(user_id=user_id, tag="", count=total_stories_count)
return dict(total=total_stories_count, tags=user_tags)
class MFetchHistory(mongo.Document):
feed_id = mongo.IntField(unique=True)
feed_fetch_history = mongo.DynamicField()

View file

@ -3096,6 +3096,7 @@ body {
.NB-sideoption-save-wrapper.NB-active {
display: block;
height: auto;
}
.NB-narrow-content .NB-sideoption-save-wrapper {
@ -7583,11 +7584,11 @@ form.opml_import_form input {
}
.NB-modal-feed-settings .NB-preference-options div {
float: left;
margin: 0 12px;
margin: 0 6px 0 0;
}
.NB-modal-feed-settings .NB-preference-options input[type=radio] {
float: left;
margin: 10px 4px;
margin: 2px 6px 0 0px;
}
.NB-modal-feed-settings .NB-preference-options label {
@ -7595,6 +7596,9 @@ form.opml_import_form input {
margin: 0 0 4px 0;
float: left;
cursor: pointer;
text-transform: uppercase;
font-size: 12px;
color: #303030;
}
.NB-modal-feed-settings .NB-preference-options img {
height: 31px;
@ -7605,6 +7609,23 @@ form.opml_import_form input {
text-transform: uppercase;
opacity: 0;
}
.NB-modal-feed-settings .NB-view-settings label {
display: block;
padding: 4px 6px;
border: 1px solid rgba(0,0,0,.2);
border-radius: 3px;
}
.NB-modal-feed-settings .NB-view-settings img {
float: left;
width: 18px;
height: 15px;
padding: 1px 0 0 0;
margin: 0 4px 0 0;
}
.NB-modal-feed-settings .NB-view-title {
margin: 0;
padding: 1px 0 0 0;
}
/* ===================== */
/* = Feedchooser Modal = */

View file

@ -874,8 +874,10 @@ NEWSBLUR.AssetModel = Backbone.Router.extend({
get_feed: function(feed_id) {
var self = this;
if (_.string.include(feed_id, 'social:')) {
if (_.string.startsWith(feed_id, 'social:')) {
return this.social_feeds.get(feed_id);
} else if (_.string.startsWith(feed_id, 'starred:')) {
return this.starred_feeds.get(feed_id);
} else {
return this.feeds.get(feed_id);
}
@ -896,6 +898,18 @@ NEWSBLUR.AssetModel = Backbone.Router.extend({
return this.feeds;
},
get_social_feeds: function() {
var self = this;
return this.social_feeds;
},
get_starred_feeds: function() {
var self = this;
return this.starred_feeds;
},
get_folders: function() {
var self = this;

View file

@ -69,21 +69,52 @@ NEWSBLUR.Modal.prototype = {
$.modal.close(callback);
},
make_feed_chooser: function() {
make_feed_chooser: function(options) {
options = options || {};
var $chooser = $.make('select', { name: 'feed', className: 'NB-modal-feed-chooser' });
var $feeds_optgroup = $.make('optgroup', { label: "Sites" });
var $social_feeds_optgroup = $.make('optgroup', { label: "Blurblogs" });
var $starred_feeds_optgroup = $.make('optgroup', { label: "Saved Tags" });
var current_feed_id = this.feed_id;
this.feeds = this.model.get_feeds();
this.feeds.each(function(feed) {
var make_feed_option = function(feed) {
if (!feed.get('feed_title')) return;
var $option = $.make('option', { value: feed.id }, feed.get('feed_title'));
$option.appendTo($chooser);
$option.appendTo(feed.is_starred() ? $starred_feeds_optgroup :
feed.is_social() ? $social_feeds_optgroup :
$feeds_optgroup);
if (feed.id == current_feed_id) {
$option.attr('selected', true);
}
});
};
this.feeds = this.model.get_feeds();
this.feeds.each(make_feed_option);
if (!options.skip_social) {
this.social_feeds = this.model.get_social_feeds();
this.social_feeds.each(make_feed_option);
}
if (!options.skip_starred) {
this.starred_feeds = this.model.get_starred_feeds();
this.starred_feeds.each(make_feed_option);
}
$('option', $feeds_optgroup).tsort();
$('option', $social_feeds_optgroup).tsort();
$('option', $starred_feeds_optgroup).tsort();
$chooser.append($feeds_optgroup);
if (!options.skip_social) {
$chooser.append($social_feeds_optgroup);
}
if (!options.skip_starred) {
$chooser.append($starred_feeds_optgroup);
}
$('option', $chooser).tsort();
return $chooser;
},

View file

@ -2973,6 +2973,16 @@
if (feed_id && unread_count == 0) {
$('.NB-menu-manage-feed-mark-read', $manage_menu).addClass('NB-disabled');
}
} else if (type == 'starred') {
$manage_menu = $.make('ul', { className: 'NB-menu-manage NB-menu-manage-feed' }, [
$.make('li', { className: 'NB-menu-separator-inverse' }),
$.make('li', { className: 'NB-menu-item NB-menu-manage-feed-settings' }, [
$.make('div', { className: 'NB-menu-manage-image' }),
$.make('div', { className: 'NB-menu-manage-title' }, 'Tag settings')
])
]);
$manage_menu.data('feed_id', feed_id);
$manage_menu.data('$feed', $item);
} else if (type == 'folder') {
$manage_menu = $.make('ul', { className: 'NB-menu-manage NB-menu-manage-folder' }, [
$.make('li', { className: 'NB-menu-separator-inverse' }),
@ -3235,6 +3245,9 @@
} else if (type == 'socialfeed') {
feed_id = options.feed_id;
inverse = options.inverse || $item.hasClass("NB-hover-inverse");
} else if (type == 'starred') {
feed_id = options.feed_id;
inverse = options.inverse || $item.hasClass("NB-hover-inverse");
} else if (type == 'story') {
story_id = options.story_id;
if ($item.hasClass('NB-hover-inverse')) inverse = true;
@ -3281,15 +3294,16 @@
$manage_menu_container.css('z-index', $("#simplemodal-container").css('z-index'));
}
$('.NB-task-manage').addClass('NB-hover');
} else if (type == 'feed' || type == 'folder' || type == 'story' || type == 'socialfeed') {
} else if (type == 'feed' || type == 'folder' || type == 'story' ||
type == 'socialfeed' || type == 'starred') {
var left, top;
NEWSBLUR.log(['menu open', $item, inverse, toplevel, type]);
// NEWSBLUR.log(['menu open', $item, inverse, toplevel, type]);
if (inverse) {
var $align = $item;
if (type == 'feed') {
left = toplevel ? 2 : -22;
top = toplevel ? 1 : 3;
} else if (type == 'socialfeed') {
} else if (type == 'socialfeed' || type == 'starred') {
left = 2;
top = 2;
} else if (type == 'folder') {
@ -3319,7 +3333,7 @@
left = toplevel ? 0 : -2;
top = toplevel ? 20 : 19;
$align = $('.NB-feedlist-manage-icon', $item);
} else if (type == 'socialfeed') {
} else if (type == 'socialfeed' || type == 'starred') {
left = toplevel ? 0 : -18;
top = toplevel ? 20 : 21;
$align = $('.NB-feedlist-manage-icon', $item);
@ -3344,7 +3358,8 @@
$manage_menu_container.stop().css({'display': 'block', 'opacity': 1});
// Create and position the arrow tab
if (type == 'feed' || type == 'folder' || type == 'story' || type == 'socialfeed') {
if (type == 'feed' || type == 'folder' || type == 'story' ||
type == 'socialfeed' || type == 'starred') {
var $arrow = $.make('div', { className: 'NB-menu-manage-arrow' }, [
$.make('div', { className: 'NB-icon' })
]);
@ -3408,7 +3423,7 @@
// Hide menu on scroll.
var $scroll;
this.flags['feed_list_showing_manage_menu'] = true;
if (type == 'feed' || type == 'socialfeed') {
if (type == 'feed' || type == 'socialfeed' || type == 'starred') {
$scroll = this.$s.$feed_list.parent();
} else if (type == 'story') {
$scroll = this.$s.$story_titles.add(this.$s.$feed_scroll);

View file

@ -45,7 +45,9 @@ _.extend(NEWSBLUR.ReaderFeedException.prototype, {
return false;
}
});
$(".NB-exception-option-page", this.$modal).toggle(this.feed.is_feed() || this.feed.is_social());
$(".NB-view-setting-original", this.$modal).toggle(this.feed.is_feed() || this.feed.is_social());
if (this.feed.get('exception_type')) {
this.$modal.removeClass('NB-modal-feed-settings');
} else {
@ -56,10 +58,13 @@ _.extend(NEWSBLUR.ReaderFeedException.prototype, {
},
get_feed_settings: function() {
if (this.feed.is_starred()) return;
var $loading = $('.NB-modal-loading', this.$modal);
$loading.addClass('NB-active');
var settings_fn = this.options.social_feed ? this.model.get_social_settings : this.model.get_feed_settings;
var settings_fn = this.options.social_feed ? this.model.get_social_settings :
this.model.get_feed_settings;
settings_fn.call(this.model, this.feed_id, _.bind(this.populate_settings, this));
},
@ -90,7 +95,7 @@ _.extend(NEWSBLUR.ReaderFeedException.prototype, {
$.make('img', { className: 'NB-modal-feed-image feed_favicon', src: $.favicon(this.feed) }),
$.make('div', { className: 'NB-modal-feed-heading' }, [
$.make('span', { className: 'NB-modal-feed-title' }, this.feed.get('feed_title')),
$.make('span', { className: 'NB-modal-feed-subscribers' },Inflector.pluralize(' subscriber', this.feed.get('num_subscribers'), true))
(this.feed.get('num_subscribers') && $.make('span', { className: 'NB-modal-feed-subscribers' },Inflector.pluralize(' subscriber', this.feed.get('num_subscribers'), true)))
])
]),
$.make('div', { className: 'NB-fieldset NB-exception-option NB-exception-option-view NB-modal-submit NB-settings-only' }, [
@ -104,23 +109,33 @@ _.extend(NEWSBLUR.ReaderFeedException.prototype, {
$.make('div', { className: 'NB-preference-label'}, [
'Reading view'
]),
$.make('div', { className: 'NB-preference-options' }, [
$.make('div', [
$.make('input', { id: 'NB-preference-view-1', type: 'radio', name: 'view_settings', value: 'page' }),
$.make('div', { className: 'NB-preference-options NB-view-settings' }, [
$.make('div', { className: "NB-view-setting-original" }, [
$.make('label', { 'for': 'NB-preference-view-1' }, [
$.make('img', { src: NEWSBLUR.Globals.MEDIA_URL+'/img/reader/preferences_view_original.png' })
$.make('input', { id: 'NB-preference-view-1', type: 'radio', name: 'view_settings', value: 'page' }),
$.make("img", { src: NEWSBLUR.Globals.MEDIA_URL+'/img/icons/circular/nav_story_original_active.png' }),
$.make("div", { className: "NB-view-title" }, "Original")
])
]),
$.make('div', [
$.make('input', { id: 'NB-preference-view-2', type: 'radio', name: 'view_settings', value: 'feed' }),
$.make('label', { 'for': 'NB-preference-view-2' }, [
$.make('img', { src: NEWSBLUR.Globals.MEDIA_URL+'/img/reader/preferences_view_feed.png' })
$.make('input', { id: 'NB-preference-view-2', type: 'radio', name: 'view_settings', value: 'feed' }),
$.make("img", { src: NEWSBLUR.Globals.MEDIA_URL+'/img/icons/circular/nav_story_feed_active.png' }),
$.make("div", { className: "NB-view-title" }, "Feed")
])
]),
$.make('div', [
$.make('input', { id: 'NB-preference-view-3', type: 'radio', name: 'view_settings', value: 'story' }),
$.make('label', { 'for': 'NB-preference-view-3' }, [
$.make('img', { src: NEWSBLUR.Globals.MEDIA_URL+'/img/reader/preferences_view_story.png' })
$.make('input', { id: 'NB-preference-view-3', type: 'radio', name: 'view_settings', value: 'text' }),
$.make("img", { src: NEWSBLUR.Globals.MEDIA_URL+'/img/icons/circular/nav_story_text_active.png' }),
$.make("div", { className: "NB-view-title" }, "Text")
])
]),
$.make('div', [
$.make('label', { 'for': 'NB-preference-view-4' }, [
$.make('input', { id: 'NB-preference-view-4', type: 'radio', name: 'view_settings', value: 'story' }),
$.make("img", { src: NEWSBLUR.Globals.MEDIA_URL+'/img/icons/circular/nav_story_story_active.png' }),
$.make("div", { className: "NB-view-title" }, "Story")
])
])
])
@ -156,14 +171,14 @@ _.extend(NEWSBLUR.ReaderFeedException.prototype, {
]),
$.make('input', { type: 'text', id: 'NB-exception-input-address', className: 'NB-exception-input-address NB-input', name: 'feed_address', value: this.feed.get('feed_address') })
]),
(!this.options.social_feed && $.make('div', { className: 'NB-exception-submit-wrapper' }, [
(this.feed.is_feed() && $.make('div', { className: 'NB-exception-submit-wrapper' }, [
$.make('div', { className: 'NB-modal-submit-button NB-modal-submit-green NB-modal-submit-address' }, 'Parse this RSS/XML Feed'),
$.make('div', { className: 'NB-error' }),
$.make('div', { className: 'NB-exception-feed-history' })
]))
])
]),
$.make('div', { className: 'NB-fieldset NB-exception-option NB-exception-option-page NB-modal-submit' }, [
($.make('div', { className: 'NB-fieldset NB-exception-option NB-exception-option-page NB-modal-submit' }, [
$.make('h5', [
$.make('div', { className: 'NB-exception-option-meta' }),
$.make('span', { className: 'NB-exception-option-option NB-exception-only' }, 'Option 3:'),
@ -178,13 +193,13 @@ _.extend(NEWSBLUR.ReaderFeedException.prototype, {
]),
$.make('input', { type: 'text', id: 'NB-exception-input-link', className: 'NB-exception-input-link NB-input', name: 'feed_link', value: this.feed.get('feed_link') })
]),
(!this.options.social_feed && $.make('div', { className: 'NB-exception-submit-wrapper' }, [
(this.feed.is_feed() && $.make('div', { className: 'NB-exception-submit-wrapper' }, [
$.make('div', { className: 'NB-modal-submit-button NB-modal-submit-green NB-modal-submit-link' }, 'Fetch Feed From Website'),
$.make('div', { className: 'NB-error' }),
$.make('div', { className: 'NB-exception-page-history' })
]))
])
]),
])),
$.make('div', { className: 'NB-fieldset NB-exception-option NB-exception-option-delete NB-exception-block-only NB-modal-submit' }, [
$.make('h5', [
$.make('span', { className: 'NB-exception-option-option NB-exception-only' }, 'Option 4:'),

View file

@ -169,8 +169,7 @@ NEWSBLUR.Views.FeedList = Backbone.View.extend({
model: feed,
type: 'feed',
depth: 0,
starred_tag: true,
disable_hover: true
starred_tag: true
}).render();
feed.views.push(feed_view);
return feed_view.el;

View file

@ -360,11 +360,14 @@ NEWSBLUR.Views.FeedTitleView = Backbone.View.extend({
show_manage_menu: function(e) {
if (this.options.feed_chooser) return;
var feed_type = this.model.is_social() ? 'socialfeed' :
this.model.is_starred() ? 'starred' :
'feed';
e.preventDefault();
e.stopPropagation();
NEWSBLUR.log(["showing manage menu", this.model.is_social() ? 'socialfeed' : 'feed', $(this.el), this, e.which, e.button]);
NEWSBLUR.reader.show_manage_menu(this.model.is_social() ? 'socialfeed' : 'feed', this.$el, {
NEWSBLUR.reader.show_manage_menu(feed_type, this.$el, {
feed_id: this.model.id,
toplevel: this.options.depth == 0,
rightclick: e.which >= 2

View file

@ -103,7 +103,7 @@ NEWSBLUR.Views.StoryDetailView = Backbone.View.extend({
resize_starred_tags: function() {
if (this.model.get('starred')) {
this.save_view.reset_height();
this.save_view.reset_height({immediate: true});
}
},

View file

@ -22,7 +22,7 @@ NEWSBLUR.Views.StorySaveView = Backbone.View.extend({
},
template: _.template('\
<div class="NB-sideoption-save-wrapper">\
<div class="NB-sideoption-save-wrapper <% if (story.get("starred")) { %>NB-active<% } %>">\
<div class="NB-sideoption-save">\
<% if (story_tags.length) { %>\
<div class="NB-sideoption-save-populate">\