fixing merge conflict

This commit is contained in:
Roy Yang 2012-06-29 13:58:39 -07:00
parent b5ccc81a5e
commit 065395b688
75 changed files with 2127 additions and 642 deletions

View file

@ -1,7 +1,7 @@
The MIT License
===============
Copyright (c) 2009-2010 Samuel Clay <samuel@ofbrooklyn.com>.
Copyright (c) 2009-2012 Samuel Clay, NewsBlur <samuel@newsblur.com>.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -145,7 +145,7 @@ these after the installation below.
4. Run the development server. At this point, all dependencies should be installed and no
additional configuration is needed. If you find that something is not working at this
point, please email the resulting output to Samuel Clay at
[samuel@ofbrooklyn.com](samuel@ofbrooklyn.com).
[samuel@newsblur.com](samuel@newsblur.com).
./manage.py runserver
@ -230,7 +230,7 @@ reader, and feed importer. To run the test suite:
## Author
* Created by [Samuel Clay](http://www.samuelclay.com).
* Email address: <samuel@ofbrooklyn.com>
* Email address: <samuel@newsblur.com>
* [@samuelclay](http://twitter.com/samuelclay) on Twitter.

View file

@ -6,7 +6,7 @@ import urllib, urllib2
import cookielib
import json
__author__ = "Dananjaya Ramanayake <dananjaya86@gmail.com>, Samuel Clay <samuel@ofbrooklyn.com>"
__author__ = "Dananjaya Ramanayake <dananjaya86@gmail.com>, Samuel Clay <samuel@newsblur.com>"
__version__ = "1.0"
API_URL = "http://www.newsblur.com/"

View file

@ -55,7 +55,7 @@
"groups": [],
"user_permissions": [],
"password": "sha1$7b94b$ac9e6cf08d0fa16a67e56e319c0935aeb26db2a2",
"email": "samuel@ofbrooklyn.com",
"email": "samuel@newsblur.com",
"date_joined": "2009-01-04 17:32:58"
}
}

View file

@ -13,7 +13,7 @@
"groups": [],
"user_permissions": [],
"password": "sha1$7b94b$ac9e6cf08d0fa16a67e56e319c0935aeb26db2a2",
"email": "samuel@ofbrooklyn.com",
"email": "samuel@newsblur.com",
"date_joined": "2009-01-04 17:32:58"
}
}

View file

@ -150,7 +150,7 @@
"groups": [],
"user_permissions": [],
"password": "sha1$7b94b$ac9e6cf08d0fa16a67e56e319c0935aeb26db2a2",
"email": "samuel@ofbrooklyn.com",
"email": "samuel@newsblur.com",
"date_joined": "2009-01-04 17:32:58"
}
},

View file

@ -16,6 +16,7 @@ from django.conf import settings
from django.core.mail import mail_admins
from django.core.validators import email_re
from django.core.mail import EmailMultiAlternatives
from django.contrib.sites.models import Site
from mongoengine.queryset import OperationError
from pymongo.helpers import OperationFailure
from operator import itemgetter
@ -33,7 +34,9 @@ try:
from apps.rss_feeds.models import Feed, MFeedPage, DuplicateFeed, MStory, MStarredStory, FeedLoadtime
except:
pass
from apps.social.models import MSharedStory, MSocialProfile, MSocialSubscription, MActivity
from apps.social.models import MSharedStory, MSocialProfile, MSocialServices
from apps.social.models import MSocialSubscription, MActivity
from apps.social.views import load_social_page
from utils import json_functions as json
from utils.user_functions import get_user, ajax_login_required
from utils.feed_functions import relative_timesince
@ -48,9 +51,20 @@ from vendor.timezones.utilities import localtime_for_timezone
SINGLE_DAY = 60*60*24
@never_cache
@render_to('reader/feeds.xhtml')
def index(request):
if request.method == "GET" and request.subdomain and request.subdomain != 'dev':
username = request.subdomain
try:
if '.' in username:
username = username.split('.')[0]
user = User.objects.get(username__iexact=username)
except User.DoesNotExist:
return HttpResponseRedirect('http://%s%s' % (
Site.objects.get_current().domain.replace('www', 'dev'),
reverse('index')))
return load_social_page(request, user_id=user.pk, username=request.subdomain)
# XXX TODO: Remove me on launch.
if request.method == "GET" and request.user.is_anonymous() and not request.REQUEST.get('letmein'):
return {}, 'reader/social_signup.xhtml'
@ -211,6 +225,7 @@ def load_feeds(request):
}
social_feeds = MSocialSubscription.feeds(**social_params)
social_profile = MSocialProfile.profile(user.pk)
social_services = MSocialServices.profile(user.pk)
user.profile.dashboard_date = datetime.datetime.now()
user.profile.save()
@ -219,6 +234,7 @@ def load_feeds(request):
'feeds': feeds.values() if version == 2 else feeds,
'social_feeds': social_feeds,
'social_profile': social_profile,
'social_services': social_services,
'folders': json.decode(folders.folders),
'starred_count': starred_count,
}
@ -228,11 +244,11 @@ def load_feeds(request):
def load_feed_favicons(request):
user = get_user(request)
feed_ids = request.REQUEST.getlist('feed_ids')
user_subs = UserSubscription.objects.select_related('feed').filter(user=user, active=True)
if feed_ids and len(feed_ids) > 0:
user_subs = user_subs.filter(feed__in=feed_ids)
if not feed_ids:
user_subs = UserSubscription.objects.select_related('feed').filter(user=user, active=True)
feed_ids = [sub['feed__pk'] for sub in user_subs.values('feed__pk')]
feed_ids = [sub['feed__pk'] for sub in user_subs.values('feed__pk')]
feed_icons = dict([(i.feed_id, i.data) for i in MFeedIcon.objects(feed_id__in=feed_ids)])
return feed_icons
@ -240,10 +256,13 @@ def load_feed_favicons(request):
def load_feeds_flat(request):
user = request.user
include_favicons = request.REQUEST.get('include_favicons', False)
update_counts = request.REQUEST.get('update_counts', False)
feeds = {}
iphone_version = "1.2"
if include_favicons == 'false': include_favicons = False
if update_counts == 'false': update_counts = False
if not user.is_authenticated():
return HttpResponseForbidden()
@ -285,7 +304,23 @@ def load_feeds_flat(request):
make_feeds_folder(folder, flat_folder_name, depth+1)
make_feeds_folder(folders)
data = dict(flat_folders=flat_folders, feeds=feeds, user=user.username, iphone_version=iphone_version)
social_params = {
'user_id': user.pk,
'include_favicon': include_favicons,
'update_counts': update_counts,
}
social_feeds = MSocialSubscription.feeds(**social_params)
social_profile = MSocialProfile.profile(user.pk)
data = {
"flat_folders": flat_folders,
"feeds": feeds,
"social_feeds": social_feeds,
"social_profile": social_profile,
"user": user.username,
"iphone_version": iphone_version,
}
return data
@ratelimit(minutes=1, requests=20)
@ -643,14 +678,20 @@ def load_river_stories(request):
# starred_stories = {}
# Intelligence classifiers for all feeds involved
classifier_feeds = list(MClassifierFeed.objects(user_id=user.pk,
feed_id__in=found_feed_ids))
classifier_authors = list(MClassifierAuthor.objects(user_id=user.pk,
if found_feed_ids:
classifier_feeds = list(MClassifierFeed.objects(user_id=user.pk,
feed_id__in=found_feed_ids))
classifier_titles = list(MClassifierTitle.objects(user_id=user.pk,
classifier_authors = list(MClassifierAuthor.objects(user_id=user.pk,
feed_id__in=found_feed_ids))
classifier_titles = list(MClassifierTitle.objects(user_id=user.pk,
feed_id__in=found_feed_ids))
classifier_tags = list(MClassifierTag.objects(user_id=user.pk,
feed_id__in=found_feed_ids))
classifier_tags = list(MClassifierTag.objects(user_id=user.pk,
feed_id__in=found_feed_ids))
else:
classifier_feeds = []
classifier_authors = []
classifier_titles = []
classifier_tags = []
classifiers = sort_classifiers_by_feed(user=user, feed_ids=found_feed_ids,
classifier_feeds=classifier_feeds,
classifier_authors=classifier_authors,

View file

@ -20,7 +20,7 @@
"groups": [],
"user_permissions": [],
"password": "sha1$d5473$d07ce4495b088ff0f41a62d5113d0189ce8f0096",
"email": "samuel@ofbrooklyn.com",
"email": "samuel@newsblur.com",
"date_joined": "2011-07-18 00:23:49"
}
}

View file

@ -125,7 +125,7 @@
"groups": [],
"user_permissions": [],
"password": "sha1$7b94b$ac9e6cf08d0fa16a67e56e319c0935aeb26db2a2",
"email": "samuel@ofbrooklyn.com",
"email": "samuel@newsblur.com",
"date_joined": "2009-01-04 17:32:58"
}
}

View file

@ -933,6 +933,8 @@ class Feed(models.Model):
text = re.sub(r'\n+', '\n\n', text)
text = re.sub(r'\t+', '\t', text)
story['text'] = text
if '<ins' in story['story_content'] or '<del' in story['story_content']:
story['has_modifications'] = True
return story

View file

@ -24,6 +24,7 @@ from vendor import facebook
from vendor import tweepy
from utils import log as logging
from utils.feed_functions import relative_timesince
from utils.story_functions import truncate_chars
from utils import json_functions as json
RECOMMENDATIONS_LIMIT = 5
@ -125,13 +126,19 @@ class MSocialProfile(mongo.Document):
if not self.username:
self.import_user_fields()
if not self.subscription_count:
self.count(skip_save=True)
self.count_follows(skip_save=True)
if self.bio and len(self.bio) > MSocialProfile.bio.max_length:
self.bio = self.bio[:80]
super(MSocialProfile, self).save(*args, **kwargs)
if self.user_id not in self.following_user_ids:
self.follow_user(self.user_id)
self.count()
self.count_follows()
@property
def blurblog_url(self):
return "http://%s.%s" % (
self.username_slug,
Site.objects.get_current().domain.replace('www', 'dev'))
def recommended_users(self):
r = redis.Redis(connection_pool=settings.REDIS_POOL)
@ -221,7 +228,7 @@ class MSocialProfile(mongo.Document):
profile = cls.objects.get(user_id=user_id)
except cls.DoesNotExist:
return {}
return profile.to_json(full=True)
return profile.to_json(include_follows=True)
@classmethod
def profiles(cls, user_ids):
@ -259,7 +266,7 @@ class MSocialProfile(mongo.Document):
return params
def page(self):
params = self.to_json(full=True)
params = self.to_json(include_follows=True)
params.update({
'feed_title': self.title,
'custom_css': self.custom_css,
@ -271,8 +278,17 @@ class MSocialProfile(mongo.Document):
if self.photo_url:
return self.photo_url
return settings.MEDIA_URL + 'img/reader/default_profile_photo.png'
@property
def email_photo_url(self):
if self.photo_url:
if self.photo_url.startswith('//'):
self.photo_url = 'http:' + self.photo_url
return self.photo_url
domain = Site.objects.get_current().domain.replace('www', 'dev')
return 'http://' + domain + settings.MEDIA_URL + 'img/reader/default_profile_photo.png'
def to_json(self, compact=False, full=False, common_follows_with_user=None):
def to_json(self, compact=False, include_follows=False, common_follows_with_user=None):
# domain = Site.objects.get_current().domain
domain = Site.objects.get_current().domain.replace('www', 'dev')
params = {
@ -284,8 +300,7 @@ class MSocialProfile(mongo.Document):
'feed_title': self.title,
'feed_address': "http://%s%s" % (domain, reverse('shared-stories-rss-feed',
kwargs={'user_id': self.user_id, 'username': self.username_slug})),
'feed_link': "http://%s%s" % (domain, reverse('load-social-page',
kwargs={'user_id': self.user_id, 'username': self.username_slug})),
'feed_link': self.blurblog_url,
}
if not compact:
params.update({
@ -300,11 +315,11 @@ class MSocialProfile(mongo.Document):
'stories_last_month': self.stories_last_month,
'average_stories_per_month': self.average_stories_per_month,
})
if full:
if include_follows:
params.update({
'photo_service': self.photo_service,
'following_user_ids': self.following_user_ids,
'follower_user_ids': self.follower_user_ids,
'following_user_ids': self.following_user_ids_without_self,
'follower_user_ids': self.follower_user_ids_without_self,
})
if common_follows_with_user:
with_user, _ = MSocialProfile.objects.get_or_create(user_id=common_follows_with_user)
@ -317,16 +332,28 @@ class MSocialProfile(mongo.Document):
return params
@property
def following_user_ids_without_self(self):
if self.user_id in self.following_user_ids:
return [u for u in self.following_user_ids if u != self.user_id]
return self.following_user_ids
@property
def follower_user_ids_without_self(self):
if self.user_id in self.follower_user_ids:
return [u for u in self.follower_user_ids if u != self.user_id]
return self.follower_user_ids
def import_user_fields(self, skip_save=False):
user = User.objects.get(pk=self.user_id)
self.username = user.username
self.email = user.email
def count(self, skip_save=False):
def count_follows(self, skip_save=False):
self.subscription_count = UserSubscription.objects.filter(user__pk=self.user_id).count()
self.shared_stories_count = MSharedStory.objects.filter(user_id=self.user_id).count()
self.following_count = len(self.following_user_ids)
self.follower_count = len(self.follower_user_ids)
self.following_count = len(self.following_user_ids_without_self)
self.follower_count = len(self.follower_user_ids_without_self)
if not skip_save:
self.save()
@ -340,7 +367,7 @@ class MSocialProfile(mongo.Document):
self.following_user_ids.append(user_id)
if user_id in self.unfollowed_user_ids:
self.unfollowed_user_ids.remove(user_id)
self.count()
self.count_follows()
self.save()
if self.user_id == user_id:
@ -349,7 +376,7 @@ class MSocialProfile(mongo.Document):
followee, _ = MSocialProfile.objects.get_or_create(user_id=user_id)
if self.user_id not in followee.follower_user_ids:
followee.follower_user_ids.append(self.user_id)
followee.count()
followee.count_follows()
followee.save()
following_key = "F:%s:F" % (self.user_id)
@ -364,6 +391,9 @@ class MSocialProfile(mongo.Document):
subscription_user_id=user_id)
socialsub.needs_unread_recalc = True
socialsub.save()
from apps.social.tasks import EmailNewFollower
EmailNewFollower.delay(follower_user_id=self.user_id, followee_user_id=user_id)
def is_following_user(self, user_id):
return user_id in self.following_user_ids
@ -382,13 +412,13 @@ class MSocialProfile(mongo.Document):
self.following_user_ids.remove(user_id)
if user_id not in self.unfollowed_user_ids:
self.unfollowed_user_ids.append(user_id)
self.count()
self.count_follows()
self.save()
followee = MSocialProfile.objects.get(user_id=user_id)
if self.user_id in followee.follower_user_ids:
followee.follower_user_ids.remove(self.user_id)
followee.count()
followee.count_follows()
followee.save()
following_key = "F:%s:F" % (self.user_id)
@ -408,7 +438,49 @@ class MSocialProfile(mongo.Document):
follows_inter = [int(f) for f in follows_inter]
follows_diff = [int(f) for f in follows_diff]
if user_id in follows_inter:
follows_inter.remove(user_id)
if user_id in follows_diff:
follows_diff.remove(user_id)
return follows_inter, follows_diff
def send_email_for_new_follower(self, follower_user_id):
user = User.objects.get(pk=self.user_id)
if not user.email or not user.profile.send_emails:
return
follower_profile = MSocialProfile.objects.get(user_id=follower_user_id)
photo_url = follower_profile.profile_photo_url
if 'graph.facebook.com' in photo_url:
follower_profile.photo_url = photo_url + '?type=large'
elif 'twimg' in photo_url:
follower_profile.photo_url = photo_url.replace('_normal', '')
common_followers, _ = self.common_follows(follower_user_id, direction='followers')
common_followings, _ = self.common_follows(follower_user_id, direction='following')
common_followers.remove(self.user_id)
common_followings.remove(self.user_id)
common_followers = MSocialProfile.profiles(common_followers)
common_followings = MSocialProfile.profiles(common_followings)
data = {
'user': user,
'follower_profile': follower_profile,
'common_followers': common_followers,
'common_followings': common_followings,
}
text = render_to_string('mail/email_new_follower.txt', data)
html = render_to_string('mail/email_new_follower.xhtml', data)
subject = "%s is now following your Blurblog on NewsBlur!" % follower_profile.username
msg = EmailMultiAlternatives(subject, text,
from_email='NewsBlur <%s>' % settings.HELLO_EMAIL,
to=['%s <%s>' % (user.username, user.email)])
msg.attach_alternative(html, "text/html")
msg.send()
logging.user(user, "~BB~FM~SBSending email for new follower: %s" % follower_profile.username)
def save_feed_story_history_statistics(self):
"""
@ -854,6 +926,7 @@ class MSharedStory(mongo.Document):
story_permalink = mongo.StringField()
story_guid = mongo.StringField(unique_with=('user_id',))
story_tags = mongo.ListField(mongo.StringField(max_length=250))
posted_to_services = mongo.ListField(mongo.StringField(max_length=20))
meta = {
'collection': 'shared_stories',
@ -891,7 +964,7 @@ class MSharedStory(mongo.Document):
super(MSharedStory, self).save(*args, **kwargs)
author, _ = MSocialProfile.objects.get_or_create(user_id=self.user_id)
author.count()
author.count_follows()
MActivity.new_shared_story(user_id=self.user_id, story_title=self.story_title,
comments=self.comments, story_feed_id=self.story_feed_id,
@ -902,9 +975,18 @@ class MSharedStory(mongo.Document):
share_key = "S:%s:%s" % (self.story_feed_id, self.guid_hash)
r.srem(share_key, self.user_id)
comment_key = "C:%s:%s" % (self.story_feed_id, self.guid_hash)
r.srem(comment_key, self.user_id)
MActivity.remove_shared_story(user_id=self.user_id, story_feed_id=self.story_feed_id,
story_id=self.story_guid)
super(MSharedStory, self).delete(*args, **kwargs)
def set_source_user_id(self, source_user_id, original_comments=None):
if source_user_id == self.user_id:
return
def find_source(source_user_id, seen_user_ids):
parent_shared_story = MSharedStory.objects.filter(user_id=source_user_id,
story_guid=self.story_guid,
@ -1104,6 +1186,39 @@ class MSharedStory(mongo.Document):
profiles = [profile.to_json(compact=True) for profile in profiles]
return stories, profiles
def blurblog_permalink(self):
profile = MSocialProfile.objects.get(user_id=self.user_id)
return "%s/story/%s" % (
profile.blurblog_url,
self.guid_hash[:6]
)
def generate_post_to_service_message(self):
message = self.comments
if not message or len(message) < 1:
message = self.story_title
message = truncate_chars(message, 116)
message += " " + self.blurblog_permalink()
print message
return message
def post_to_service(self, service):
if service in self.posted_to_services:
return
message = self.generate_post_to_service_message()
social_service = MSocialServices.objects.get(user_id=self.user_id)
if service == 'twitter':
posted = social_service.post_to_twitter(message)
elif service == 'facebook':
posted = social_service.post_to_facebook(message)
if posted:
self.posted_to_services.append(service)
self.save()
class MSocialServices(mongo.Document):
@ -1154,6 +1269,14 @@ class MSocialServices(mongo.Document):
}
}
@classmethod
def profile(cls, user_id):
try:
profile = cls.objects.get(user_id=user_id)
except cls.DoesNotExist:
return {}
return profile.to_json()
def twitter_api(self):
twitter_consumer_key = settings.TWITTER_CONSUMER_KEY
twitter_consumer_secret = settings.TWITTER_CONSUMER_SECRET
@ -1189,7 +1312,7 @@ class MSocialServices(mongo.Document):
profile.bio = profile.bio or twitter_user.description
profile.website = profile.website or twitter_user.url
profile.save()
profile.count()
profile.count_follows()
if not profile.photo_url or not profile.photo_service:
self.set_photo('twitter')
@ -1216,7 +1339,7 @@ class MSocialServices(mongo.Document):
profile.bio = profile.bio or facebook_user.get('bio')
profile.website = profile.website or facebook_user.get('website')
profile.save()
profile.count()
profile.count_follows()
if not profile.photo_url or not profile.photo_service:
self.set_photo('facebook')
@ -1300,7 +1423,26 @@ class MSocialServices(mongo.Document):
hashlib.md5(user.email).hexdigest()
profile.save()
return profile
def post_to_twitter(self, message):
try:
api = self.twitter_api()
api.update_status(status=message)
except tweepy.TweepError, e:
print e
return
return True
def post_to_facebook(self, message):
try:
api = self.facebook_api()
api.put_wall_post(message=message)
except facebook.GraphAPIError, e:
print e
return
return True
class MInteraction(mongo.Document):
user_id = mongo.IntField()
@ -1578,3 +1720,17 @@ class MActivity(mongo.Document):
if share_date:
a.date = share_date
a.save()
@classmethod
def remove_shared_story(cls, user_id, story_feed_id, story_id):
try:
a = cls.objects.get(user_id=user_id,
with_user_id=user_id,
category='sharedstory',
feed_id=story_feed_id,
content_id=story_id)
except cls.DoesNotExist:
return
a.delete()

18
apps/social/tasks.py Normal file
View file

@ -0,0 +1,18 @@
from celery.task import Task
from apps.social.models import MSharedStory, MSocialProfile
class PostToService(Task):
def run(self, shared_story_id, service):
try:
shared_story = MSharedStory.objects.get(id=shared_story_id)
shared_story.post_to_service(service)
except MSharedStory.DoesNotExist:
print "Story not found (%s). Can't post to: %s" % (shared_story_id, service)
class EmailNewFollower(Task):
def run(self, follower_user_id, followee_user_id):
user_profile = MSocialProfile.objects.get(user_id=followee_user_id)
user_profile.send_email_for_new_follower(follower_user_id)

View file

@ -4,6 +4,7 @@ from apps.social import views
urlpatterns = patterns('',
url(r'^request_invite/?$', views.request_invite, name='request-invite'),
url(r'^share_story/?$', views.mark_story_as_shared, name='mark-story-as-shared'),
url(r'^unshare_story/?$', views.mark_story_as_unshared, name='mark-story-as-unshared'),
url(r'^load_user_friends/?$', views.load_user_friends, name='load-user-friends'),
url(r'^profile/?$', views.profile, name='profile'),
url(r'^load_user_profile/?$', views.load_user_profile, name='load-user-profile'),

View file

@ -10,6 +10,7 @@ from django.conf import settings
from apps.rss_feeds.models import MStory, Feed, MStarredStory
from apps.social.models import MSharedStory, MSocialServices, MSocialProfile, MSocialSubscription, MCommentReply
from apps.social.models import MRequestInvite, MInteraction, MActivity
from apps.social.tasks import PostToService
from apps.analyzer.models import MClassifierTitle, MClassifierAuthor, MClassifierFeed, MClassifierTag
from apps.analyzer.models import apply_classifier_titles, apply_classifier_feeds, apply_classifier_authors, apply_classifier_tags
from apps.analyzer.models import get_classifiers_for_user, sort_classifiers_by_feed
@ -172,11 +173,18 @@ def load_social_page(request, user_id, username=None):
page = request.REQUEST.get('page')
if page: offset = limit * (int(page) - 1)
social_profile = MSocialProfile.objects.get(user_id=social_user_id)
mstories = MSharedStory.objects(user_id=social_user.pk).order_by('-shared_date')[offset:offset+limit]
stories = Feed.format_stories(mstories)
if not stories:
return dict(stories=[])
return {
"user": user,
"stories": [],
"feeds": {},
"social_user": social_user,
"social_profile": social_profile.page(),
}
story_feed_ids = list(set(s['story_feed_id'] for s in stories))
feeds = Feed.objects.filter(pk__in=story_feed_ids)
@ -189,7 +197,19 @@ def load_social_page(request, user_id, username=None):
story['shared_date'] = shared_date
stories, profiles = MSharedStory.stories_with_comments_and_profiles(stories, user, check_all=True)
social_profile = MSocialProfile.objects.get(user_id=social_user_id)
profiles = dict([(p['user_id'], p) for p in profiles])
for s, story in enumerate(stories):
for u, user_id in enumerate(story['shared_by_friends']):
stories[s]['shared_by_friends'][u] = profiles[user_id]
for u, user_id in enumerate(story['shared_by_public']):
stories[s]['shared_by_public'][u] = profiles[user_id]
for c, comment in enumerate(story['comments']):
stories[s]['comments'][c]['user'] = profiles[comment['user_id']]
if comment['source_user_id']:
stories[s]['comments'][c]['source_user'] = profiles[comment['source_user_id']]
for r, reply in enumerate(comment['replies']):
stories[s]['comments'][c]['replies'][r]['user'] = profiles[reply['user_id']]
params = {
'user': user,
@ -226,10 +246,14 @@ def mark_story_as_shared(request):
story_id = request.POST['story_id']
comments = request.POST.get('comments', '')
source_user_id = request.POST.get('source_user_id')
post_to_services = request.POST.getlist('post_to_services')
story = MStory.objects(story_feed_id=feed_id, story_guid=story_id).limit(1).first()
if not story:
return {'code': -1, 'message': 'Story not found. Reload this site.'}
return {
'code': -1,
'message': 'The original story is gone. This would be a nice bug to fix. Speak up.'
}
shared_story = MSharedStory.objects.filter(user_id=request.user.pk,
story_feed_id=feed_id,
@ -240,7 +264,8 @@ def mark_story_as_shared(request):
story_values = dict(user_id=request.user.pk, comments=comments,
has_comments=bool(comments), **story_db)
shared_story = MSharedStory.objects.create(**story_values)
shared_story.set_source_user_id(source_user_id)
if source_user_id:
shared_story.set_source_user_id(int(source_user_id))
socialsubs = MSocialSubscription.objects.filter(subscription_user_id=request.user.pk)
for socialsub in socialsubs:
socialsub.needs_unread_recalc = True
@ -248,11 +273,9 @@ def mark_story_as_shared(request):
logging.user(request, "~FCSharing ~FM%s: ~SB~FB%s" % (story.story_title[:20], comments[:30]))
else:
shared_story = shared_story[0]
# original_comments = shared_story.comments
shared_story.comments = comments
shared_story.has_comments = bool(comments)
shared_story.save()
# shared_story.set_source_user_id(source_user_id, original_comments=original_comments)
logging.user(request, "~FCUpdating shared story ~FM%s: ~SB~FB%s" % (
story.story_title[:20], comments[:30]))
@ -264,8 +287,48 @@ def mark_story_as_shared(request):
story = stories[0]
story['shared_comments'] = shared_story['comments'] or ""
if post_to_services:
for service in post_to_services:
if service not in shared_story.posted_to_services:
PostToService.delay(shared_story_id=shared_story.id, service=service)
return {'code': code, 'story': story, 'user_profiles': profiles}
@ajax_login_required
@json.json_view
def mark_story_as_unshared(request):
feed_id = int(request.POST['feed_id'])
story_id = request.POST['story_id']
story = MStory.objects(story_feed_id=feed_id, story_guid=story_id).limit(1).first()
if not story:
return {'code': -1, 'message': 'Story not found. Reload this site.'}
try:
shared_story = MSharedStory.objects.get(user_id=request.user.pk,
story_feed_id=feed_id,
story_guid=story_id)
except MSharedStory.DoesNotExist:
return {'code': -1, 'message': 'Shared story not found.'}
socialsubs = MSocialSubscription.objects.filter(subscription_user_id=request.user.pk)
for socialsub in socialsubs:
socialsub.needs_unread_recalc = True
socialsub.save()
logging.user(request, "~FC~SKUn-sharing ~FM%s: ~SB~FB%s" % (shared_story.story_title[:20],
shared_story.comments[:30]))
shared_story.delete()
story.count_comments()
story = Feed.format_story(story)
stories, profiles = MSharedStory.stories_with_comments_and_profiles([story],
request.user,
check_all=True)
story = stories[0]
return {'code': 1, 'message': "Story unshared.", 'story': story, 'user_profiles': profiles}
@ajax_login_required
@json.json_view
def save_comment_reply(request):
@ -354,7 +417,8 @@ def profile(request):
user = get_user(request.user)
user_id = request.GET.get('user_id', user.pk)
user_profile = MSocialProfile.objects.get(user_id=user_id)
user_profile = user_profile.to_json(full=True, common_follows_with_user=user.pk)
user_profile.count_follows()
user_profile = user_profile.to_json(include_follows=True, common_follows_with_user=user.pk)
profile_ids = set(user_profile['followers_youknow'] + user_profile['followers_everybody'] +
user_profile['following_youknow'] + user_profile['following_everybody'])
profiles = MSocialProfile.profiles(profile_ids)
@ -387,7 +451,7 @@ def load_user_profile(request):
return {
'services': social_services,
'user_profile': social_profile.to_json(full=True),
'user_profile': social_profile.to_json(include_follows=True),
}
@ajax_login_required
@ -406,7 +470,7 @@ def save_user_profile(request):
logging.user(request, "~BB~FRSaving social profile")
return dict(code=1, user_profile=profile.to_json(full=True))
return dict(code=1, user_profile=profile.to_json(include_follows=True))
@json.json_view
def load_user_friends(request):
@ -420,7 +484,7 @@ def load_user_friends(request):
return {
'services': social_services,
'autofollow': social_services.autofollow,
'user_profile': social_profile.to_json(full=True),
'user_profile': social_profile.to_json(include_follows=True),
'following_profiles': following_profiles,
'follower_profiles': follower_profiles,
'recommended_users': recommended_users,
@ -459,7 +523,7 @@ def follow(request):
logging.user(request, "~BB~FRFollowing: %s" % follow_profile.username)
return {
"user_profile": profile.to_json(full=True),
"user_profile": profile.to_json(include_follows=True),
"follow_profile": follow_profile.to_json(common_follows_with_user=request.user.pk),
"follow_subscription": follow_subscription,
}
@ -489,7 +553,7 @@ def unfollow(request):
logging.user(request, "~BB~FRUnfollowing: %s" % unfollow_profile.username)
return {
'user_profile': profile.to_json(full=True),
'user_profile': profile.to_json(include_follows=True),
'unfollow_profile': unfollow_profile.to_json(common_follows_with_user=request.user.pk),
}
@ -518,7 +582,7 @@ def shared_stories_rss_feed(request, user_id, username):
social_profile = MSocialProfile.objects.get(user_id=user_id)
data = {}
data['title'] = social_profile.blog_title
data['title'] = social_profile.title
link = reverse('shared-stories-public', kwargs={'username': user.username})
data['link'] = "http://www.newsblur.com/%s" % link
data['description'] = "Stories shared by %s on NewsBlur." % user.username
@ -600,4 +664,4 @@ def load_interactions(request):
return {
'interactions': interactions,
'page': page,
}
}

View file

@ -50,7 +50,7 @@ javascripts:
- media/js/vendor/jquery.flot.js
- media/js/vendor/jquery.tipsy.js
- media/js/vendor/jquery.chosen.js
- media/js/vendor/jquery.linkify.js
# - media/js/vendor/jquery.linkify.js
- media/js/vendor/bootstrap.*.js
- media/js/vendor/audio.js
- media/js/vendor/socket.io-client.*.js
@ -90,6 +90,11 @@ javascripts:
- media/js/vendor/jquery.tinysort.js
- media/js/vendor/jquery.simplemodal-1.3.js
- media/js/vendor/jquery.corners.js
blurblog:
- media/js/vendor/jquery-*.js
- media/js/vendor/underscore-*.js
- media/js/vendor/underscore.string.js
- media/js/vendor/backbone-*.js
stylesheets:
common:
@ -104,4 +109,6 @@ stylesheets:
- media/css/mobile/mobile.css
bookmarklet:
- media/css/bookmarklet/reset.css
- media/css/modals.css
- media/css/modals.css
blurblog:
- media/css/social/social_page.css

View file

@ -37,7 +37,7 @@
"groups": [],
"user_permissions": [],
"password": "sha1$d5473$d07ce4495b088ff0f41a62d5113d0189ce8f0096",
"email": "samuel@ofbrooklyn.com",
"email": "samuel@newsblur.com",
"date_joined": "2011-07-18 00:23:49"
}
},

View file

@ -8,6 +8,8 @@
199.15.252.50 db02 db02.newsblur.com
199.15.253.226 db03 db03.newsblur.com
199.15.249.98 db04 db04.newsblur.com
199.15.249.99 db05 db05.newsblur.com
199.15.249.100 db06 db06.newsblur.com
199.15.250.231 task01 task01.newsblur.com
199.15.250.250 task02 task02.newsblur.com
199.15.250.233 task03 task03.newsblur.com

View file

@ -14,7 +14,7 @@ logappend=true
#port = 27017
slowms=1000
slowms=100
rest = true
#profile = 2
@ -88,5 +88,3 @@ noauth = true
# in replica set configuration, specify the name of the replica set
replSet = nbset
journal = true

View file

@ -1,36 +1,40 @@
upstream app_server {
server 127.0.0.1:8000 fail_timeout=10 max_fails=3 ;
server app02:8000 fail_timeout=10 max_fails=3 down;
}
upstream icon_server {
server 127.0.0.1:3030 fail_timeout=10 max_fails=3 ;
server 127.0.0.1:3030 fail_timeout=2 max_fails=3;
server 127.0.0.1:8000 backup;
}
# server {
# server_name newsblur.com;
# rewrite ^(.*) http://www.newsblur.com$1 permanent;
# }
server {
listen 80;
listen 443 default_server ssl;
ssl on;
# ssl on;
ssl_certificate /home/sclay/newsblur/config/certificates/newsblur.com.crt;
ssl_certificate_key /home/sclay/newsblur/config/certificates/newsblur.com.key;
client_max_body_size 4M;
server_name www.newsblur.com push.newsblur.com 199.15.250.228 127.0.0.1;
server_name www.newsblur.com newsblur.com dev.newsblur.com *.newsblur.com;
# if ($host = 'newsblur.com') {
# rewrite ^/(.*)$ https://www.newsblur.com/$1 permanent;
# }
if (-f /home/sclay/newsblur/media/maintenance.html) {
return 503;
}
error_page 502 @down;
location @down {
root /home/sclay/newsblur/;
rewrite ^(.*)$ /templates/502.html break;
}
error_page 503 @maintenance;
location @maintenance {
rewrite ^(.*)$ /home/sclay/newsblur/media/maintenance.html break;
root /home/sclay/newsblur/;
rewrite ^(.*)$ /media/maintenance.html break;
}
location /media/ {
@ -46,14 +50,14 @@ server {
}
location /favicon.ico {
alias /home/sclay/newsblur/media/img/favicon.png;
expires max;
access_log off;
alias /home/sclay/newsblur/media/img/favicon.png;
expires max;
access_log off;
}
location ^~ /crossdomain.xml {
expires max;
alias /home/sclay/newsblur/media/crossdomain.xml;
alias /home/sclay/newsblur/media/crossdomain.xml;
types {
text/x-cross-domain-policy xml;
}
@ -61,19 +65,11 @@ server {
location ^~ /robots.txt {
expires max;
alias /home/sclay/newsblur/media/robots.txt;
alias /home/sclay/newsblur/media/robots.txt;
}
location /munin/ {
alias /var/cache/munin/www/;
}
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
allow 199.15.250.228;
deny all;
alias /var/cache/munin/www/;
}
location ^~ /rss_feeds/icon/ {
@ -82,18 +78,10 @@ server {
proxy_set_header Host $http_host;
proxy_redirect off;
if (!-f $request_filename) {
proxy_pass http://icon_server;
break;
}
proxy_pass http://icon_server;
}
location / {
if (-f /home/sclay/newsblur/media/maintenance.html) {
return 503;
}
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;

View file

@ -38,15 +38,15 @@
# The default values of these variables are driven from the -D command-line
# option or PGDATA environment variable, represented here as ConfigDir.
data_directory = '/var/lib/postgresql/9.0/main' # use data in another directory
data_directory = '/var/lib/postgresql/9.1/main' # use data in another directory
# (change requires restart)
hba_file = '/etc/postgresql/9.0/main/pg_hba.conf' # host-based authentication file
hba_file = '/etc/postgresql/9.1/main/pg_hba.conf' # host-based authentication file
# (change requires restart)
ident_file = '/etc/postgresql/9.0/main/pg_ident.conf' # ident configuration file
ident_file = '/etc/postgresql/9.1/main/pg_ident.conf' # ident configuration file
# (change requires restart)
# If external_pid_file is not explicitly set, no extra PID file is written.
external_pid_file = '/var/run/postgresql/9.0-main.pid' # write an extra PID file
external_pid_file = '/var/run/postgresql/9.1-main.pid' # write an extra PID file
# (change requires restart)

View file

@ -1,5 +1,5 @@
[program:celery]
command=/home/sclay/newsblur/manage.py celeryd --loglevel=INFO -Q new_feeds,push_feeds,update_feeds
command=/home/sclay/newsblur/manage.py celeryd --loglevel=INFO -Q new_feeds,work_queue,push_feeds,update_feeds
directory=/home/sclay/newsblur
user=sclay
numprocs=1

29
fabfile.py vendored
View file

@ -41,7 +41,9 @@ env.roledefs ={
'db': ['db01.newsblur.com',
'db02.newsblur.com',
'db03.newsblur.com',
'db04.newsblur.com'],
'db04.newsblur.com',
'db05.newsblur.com',
'db06.newsblur.com'],
'task': ['task01.newsblur.com',
'task02.newsblur.com',
'task03.newsblur.com',
@ -265,13 +267,12 @@ def setup_db():
setup_baremetal()
setup_db_firewall()
setup_db_motd()
# setup_rabbitmq()
copy_task_settings()
setup_memcached()
setup_postgres()
# setup_postgres()
setup_mongo()
setup_gunicorn(supervisor=False)
setup_redis()
# setup_redis()
setup_db_munin()
def setup_task():
@ -531,6 +532,14 @@ def copy_certificates():
run('mkdir -p %s/config/certificates/' % env.NEWSBLUR_PATH)
put('config/certificates/comodo/newsblur.com.crt', '%s/config/certificates/' % env.NEWSBLUR_PATH)
put('config/certificates/comodo/newsblur.com.key', '%s/config/certificates/' % env.NEWSBLUR_PATH)
def maintenance_on():
with cd(env.NEWSBLUR_PATH):
run('mv media/maintenance.html.unused media/maintenance.html')
def maintenance_off():
with cd(env.NEWSBLUR_PATH):
run('mv media/maintenance.html media/maintenance.html.unused')
# ==============
# = Setup - DB =
@ -566,17 +575,17 @@ def setup_memcached():
sudo('apt-get -y install memcached')
def setup_postgres(standby=False):
shmmax = 572506112
# sudo('apt-get -y install postgresql postgresql-client postgresql-contrib libpq-dev')
shmmax = 577060864
sudo('apt-get -y install postgresql postgresql-client postgresql-contrib libpq-dev')
put('config/postgresql%s.conf' % (
('_standby' if standby else ''),
), '/etc/postgresql/9.0/main/postgresql.conf', use_sudo=True)
), '/etc/postgresql/9.1/main/postgresql.conf', use_sudo=True)
sudo('echo "%s" > /proc/sys/kernel/shmmax' % shmmax)
sudo('echo "\nkernel.shmmax = %s" > /etc/sysctl.conf' % shmmax)
sudo('sysctl -p')
if standby:
put('config/postgresql_recovery.conf', '/var/lib/postgresql/9.0/recovery.conf', use_sudo=True)
put('config/postgresql_recovery.conf', '/var/lib/postgresql/9.1/recovery.conf', use_sudo=True)
sudo('/etc/init.d/postgresql stop')
sudo('/etc/init.d/postgresql start')
@ -587,9 +596,11 @@ def setup_mongo():
sudo('echo "deb http://downloads-distro.mongodb.org/repo/debian-sysvinit dist 10gen" >> /etc/apt/sources.list')
sudo('apt-get update')
sudo('apt-get -y install mongodb-10gen')
put('config/mongodb.prod.conf', '/etc/mongodb.conf', use_sudo=True)
sudo('/etc/init.d/mongodb restart')
def setup_redis():
redis_version = '2.4.13'
redis_version = '2.4.15'
with cd(env.VENDOR_PATH):
run('wget http://redis.googlecode.com/files/redis-%s.tar.gz' % redis_version)
run('tar -xzf redis-%s.tar.gz' % redis_version)

View file

@ -6,16 +6,16 @@ import pymongo
# ===================
ADMINS = (
('Samuel Clay', 'samuel@ofbrooklyn.com'),
('Samuel Clay', 'samuel@newsblur.com'),
)
SERVER_EMAIL = 'server@newsblur.com'
HELLO_EMAIL = 'hello@newsblur.com'
NEWSBLUR_URL = 'http://www.newsblur.com'
# ==================
# = Global Settngs =
# ==================
# ===================
# = Global Settings =
# ===================
DEBUG = True
DEBUG_ASSETS = DEBUG
@ -29,7 +29,7 @@ CACHE_BACKEND = 'dummy:///'
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
# Set this to the username that is shown on the homepage to unauthenticated users.
HOMEPAGE_USERNAME = 'conesus'
HOMEPAGE_USERNAME = 'popular'
# Google Reader OAuth API Keys
OAUTH_KEY = 'www.example.com'
@ -42,6 +42,15 @@ S3_BACKUP_BUCKET = 'newsblur_backups'
STRIPE_SECRET = "YOUR-SECRET-API-KEY"
STRIPE_PUBLISHABLE = "YOUR-PUBLISHABLE-API-KEY"
# ===============
# = Social APIs =
# ===============
FACEBOOK_APP_ID = '111111111111111'
FACEBOOK_SECRET = '99999999999999999999999999999999'
TWITTER_CONSUMER_KEY = 'ooooooooooooooooooooo'
TWITTER_CONSUMER_SECRET = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
# =============
# = Databases =
# =============
@ -80,7 +89,6 @@ BROKER_USER = "newsblur"
BROKER_PASSWORD = "newsblur"
BROKER_VHOST = "newsblurvhost"
# ===========
# = Logging =
# ===========
@ -94,19 +102,4 @@ if len(logging._handlerList) < 1:
format='%(asctime)-12s: %(message)s',
datefmt='%b %d %H:%M:%S',
handler=logging.StreamHandler)
<<<<<<< HEAD
S3_ACCESS_KEY = 'XXX'
S3_SECRET = 'SECRET'
S3_BACKUP_BUCKET = 'newsblur_backups'
# ===============
# = Social APIs =
# ===============
FACEBOOK_APP_ID = '111111111111111'
FACEBOOK_SECRET = '99999999999999999999999999999999'
TWITTER_CONSUMER_KEY = 'ooooooooooooooooooooo'
TWITTER_CONSUMER_SECRET = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
=======
>>>>>>> jammit

View file

@ -378,11 +378,12 @@ body.NB-theme-serif #story_pane .NB-feed-story-content {
.NB-feedlists .NB-socialfeeds {
border-bottom: 1px solid #A0A0A0;
}
..NB-feedlists .NB-socialfeeds .feed .feed_title {
.NB-feedlists .NB-socialfeeds .feed .feed_title {
text-shadow: 0 1px 0 #DAE2E8;
}
.NB-feedlists .NB-socialfeeds img.feed_favicon {
border-radius: 3px;
}
/* ============= */
/* = Feed List = */
/* ============= */
@ -494,6 +495,10 @@ body.NB-theme-serif #story_pane .NB-feed-story-content {
.NB-feedlist .feed.NB-feed-inactive {
display: none;
}
.NB-feedlist .feed.NB-feed-self-blurblog,
.NB-feedlist-hide-read-feeds .NB-feedlist .feed.NB-feed-self-blurblog {
display: block;
}
.NB-feedlist .feed.NB-feed-unfetched {
}
@ -991,7 +996,9 @@ background: transparent;
width: 16px;
height: 16px;
}
#story_titles .NB-feedbar .feed.NB-feed-social .feed_favicon {
border-radius: 3px;
}
#story_titles .NB-feedbar .feed .feed_title {
/* float: left;*/
display: block;
@ -1629,6 +1636,7 @@ background: transparent;
z-index: 2;
width: 100%;
display: none;
opacity: .9;
}
.NB-view-river .NB-feed-story-view-floater {
@ -1728,7 +1736,6 @@ background: transparent;
border-bottom: 1px solid #000;
border-top: 1px solid #707070;
z-index: 2;
opacity: .9;
}
#story_pane .NB-feed-story-header-feed.NB-feed-story-river-same-feed {
z-index: 0;
@ -1770,8 +1777,12 @@ background: transparent;
/* text-shadow: 0 1px 0 #E0E0E0;*/
}
#story_pane .NB-feed-stories .NB-feed-story .NB-feed-story-content div {
max-width: 100%;
}
#story_pane .NB-feed-stories .NB-feed-story img {
max-width: 100%;
height: auto;
}
#story_pane .NB-feed-story {
position: relative;
@ -2216,10 +2227,6 @@ background: transparent;
#story_pane .NB-story-comments-public-header-wrapper {
cursor: default;
}
#story_pane .NB-story-comments-shares-teaser-wrapper {
border-top: 0;
padding-top: 0;
}
#story_pane .NB-story-comments-public-teaser,
#story_pane .NB-story-comments-public-header {
background-color: #B1B6B4;
@ -2240,6 +2247,12 @@ background: transparent;
color: #404040;
text-shadow: 0 1px 0 white;
}
#story_pane .NB-story-comments-shares-teaser-wrapper {
border-top: 0;
padding-top: 0;
}
#story_pane .NB-story-comments-shares-teaser {
background-color: whiteSmoke;
color: #202020;
@ -2322,6 +2335,11 @@ background: transparent;
#story_pane .NB-feed-stories.NB-feed-view-story .NB-feed-story {
padding: 0;
display: none;
}
#story_pane .NB-feed-stories.NB-feed-view-story .NB-feed-story.NB-selected {
display: block;
}
#story_pane .audiojs audio {
@ -2414,12 +2432,35 @@ background: transparent;
text-shadow: 0 1px 0 #F6F6F6;
color: #202020;
}
.NB-sideoption-share .NB-sideoption-share-optional {
text-transform: uppercase;
.NB-sideoption-share .NB-sideoption-share-crosspost {
margin-right: -4px;
}
.NB-sideoption-share .NB-sideoption-share-crosspost-twitter,
.NB-sideoption-share .NB-sideoption-share-crosspost-facebook {
float: right;
color: #808080;
font-size: 10px;
text-shadow: 0 1px 0 #F6F6F6;
width: 16px;
height: 16px;
margin: 0 0 0 6px;
opacity: .4;
cursor: pointer;
-webkit-filter: grayscale(100%);
display: none;
}
.NB-sideoption-share .NB-sideoption-share-crosspost-twitter:hover,
.NB-sideoption-share .NB-sideoption-share-crosspost-facebook:hover {
opacity: .7;
-webkit-filter: none;
}
.NB-sideoption-share .NB-sideoption-share-crosspost-twitter.NB-active,
.NB-sideoption-share .NB-sideoption-share-crosspost-facebook.NB-active {
opacity: 1;
-webkit-filter: none;
}
.NB-sideoption-share .NB-sideoption-share-crosspost-twitter {
background: transparent url('/media/embed/reader/twitter_icon.png') no-repeat 0 0;
}
.NB-sideoption-share .NB-sideoption-share-crosspost-facebook {
background: transparent url('/media/embed/reader/facebook_icon.png') no-repeat 0 0;
}
.NB-sideoption-share .NB-sideoption-share-comments {
width: 100%;
@ -2435,15 +2476,28 @@ background: transparent;
width: 92%;
background-color: #639510;
cursor: pointer;
-moz-box-shadow:2px 2px 0 #95AB76;
-webkit-box-shadow:2px 2px 0 #95AB76;
box-shadow:2px 2px 0 #95AB76;
-moz-box-shadow: 2px 2px 0 #95AB76;
-webkit-box-shadow: 2px 2px 0 #95AB76;
box-shadow: 2px 2px 0 #95AB76;
text-shadow: 0 1px 0 #101010;
}
.NB-sideoption-share .NB-sideoption-share-save.NB-saving {
background-color: #b5b4bB;
text-shadow: none;
}
.NB-sideoption-share .NB-sideoption-share-unshare {
color: #404040;
text-shadow: 0 1px 0 #E0E0E0;
line-height: 1;
font-size: 11px;
padding: 2px 6px;
margin: 6px 0;
width: 92%;
font-weight: normal;
-moz-box-shadow: 1px 1px 0 #95AB76;
-webkit-box-shadow: 1px 1px 0 #95AB76;
box-shadow: 1px 1px 0 #95AB76;
}
.NB-sideoption-share .NB-error {
font-size: 10px;
color: #6A1000;
@ -4995,6 +5049,14 @@ form.opml_import_form input {
text-align: center;
width: 95%;
}
.NB-menu-manage .NB-menu-manage-confirm .NB-menu-manage-story-share-unshare {
width: 95%;
text-align: center;
margin-bottom: 0;
}
.NB-menu-manage .NB-menu-manage-confirm .NB-sideoption-share-comments {
height: 28px;
}
.NB-menu-manage .NB-menu-manage-confirm .NB-add-folders {
float: left;
}
@ -5052,6 +5114,15 @@ form.opml_import_form input {
.NB-menu-manage .NB-menu-manage-story-thirdparty .NB-menu-manage-thirdparty-pinboard {
background: transparent url('/media/embed/reader/pinboard.png') no-repeat 0 0;
}
.NB-menu-manage .NB-menu-manage-story-thirdparty .NB-menu-manage-thirdparty-diigo {
background: transparent url('/media/embed/reader/diigo.png') no-repeat 0 0;
}
.NB-menu-manage .NB-menu-manage-story-thirdparty .NB-menu-manage-thirdparty-kippt {
background: transparent url('/media/embed/reader/kippt.png') no-repeat 0 0;
}
.NB-menu-manage .NB-menu-manage-story-thirdparty .NB-menu-manage-thirdparty-evernote {
background: transparent url('/media/embed/reader/evernote.png') no-repeat 0 0;
}
.NB-menu-manage .NB-menu-manage-story-thirdparty .NB-menu-manage-thirdparty-googleplus {
background: transparent url('/media/embed/reader/googleplus.png') no-repeat 0 0;
}
@ -5110,6 +5181,27 @@ form.opml_import_form input {
.NB-menu-manage .NB-menu-manage-story-thirdparty.NB-menu-manage-highlight-pinboard .NB-menu-manage-thirdparty-pinboard {
opacity: 1;
}
.NB-menu-manage .NB-menu-manage-story-thirdparty.NB-menu-manage-highlight-diigo .NB-menu-manage-image,
.NB-menu-manage .NB-menu-manage-story-thirdparty.NB-menu-manage-highlight-diigo .NB-menu-manage-thirdparty-icon {
opacity: .2;
}
.NB-menu-manage .NB-menu-manage-story-thirdparty.NB-menu-manage-highlight-diigo .NB-menu-manage-thirdparty-diigo {
opacity: 1;
}
.NB-menu-manage .NB-menu-manage-story-thirdparty.NB-menu-manage-highlight-kippt .NB-menu-manage-image,
.NB-menu-manage .NB-menu-manage-story-thirdparty.NB-menu-manage-highlight-kippt .NB-menu-manage-thirdparty-icon {
opacity: .2;
}
.NB-menu-manage .NB-menu-manage-story-thirdparty.NB-menu-manage-highlight-kippt .NB-menu-manage-thirdparty-kippt {
opacity: 1;
}
.NB-menu-manage .NB-menu-manage-story-thirdparty.NB-menu-manage-highlight-evernote .NB-menu-manage-image,
.NB-menu-manage .NB-menu-manage-story-thirdparty.NB-menu-manage-highlight-evernote .NB-menu-manage-thirdparty-icon {
opacity: .2;
}
.NB-menu-manage .NB-menu-manage-story-thirdparty.NB-menu-manage-highlight-evernote .NB-menu-manage-thirdparty-evernote {
opacity: 1;
}
.NB-menu-manage .NB-menu-manage-story-thirdparty.NB-menu-manage-highlight-googleplus .NB-menu-manage-image,
.NB-menu-manage .NB-menu-manage-story-thirdparty.NB-menu-manage-highlight-googleplus .NB-menu-manage-thirdparty-icon {
opacity: .2;
@ -6887,6 +6979,15 @@ form.opml_import_form input {
.NB-modal-preferences .NB-preference-story-share label[for=NB-preference-story-share-pinboard] {
background: transparent url('/media/embed/reader/pinboard.png') no-repeat 0 0;
}
.NB-modal-preferences .NB-preference-story-share label[for=NB-preference-story-share-diigo] {
background: transparent url('/media/embed/reader/diigo.png') no-repeat 0 0;
}
.NB-modal-preferences .NB-preference-story-share label[for=NB-preference-story-share-kippt] {
background: transparent url('/media/embed/reader/kippt.png') no-repeat 0 0;
}
.NB-modal-preferences .NB-preference-story-share label[for=NB-preference-story-share-evernote] {
background: transparent url('/media/embed/reader/evernote.png') no-repeat 0 0;
}
.NB-modal-preferences .NB-preference-story-share label[for=NB-preference-story-share-googleplus] {
background: transparent url('/media/embed/reader/googleplus.png') no-repeat 0 0;
}
@ -7173,7 +7274,6 @@ form.opml_import_form input {
margin: 12px 0;
}
.NB-static-api table {
width: 600px;
border-spacing: 0 0;
margin: 24px 0 12px 20px;
background: transparent url('/media/embed/reader/static_bullet_white.png') no-repeat 0 0;
@ -7192,7 +7292,6 @@ form.opml_import_form input {
white-space: nowrap;
/* border-bottom: 1px solid #F6F6f6;*/
}
<<<<<<< HEAD
.NB-static-api table td:last-child {
border-right: none;
}
@ -7443,7 +7542,6 @@ form.opml_import_form input {
margin-bottom: 54px;
}
}
<<<<<<< HEAD
/* ================= */
/* = Friends Modal = */

View file

@ -1,3 +1,7 @@
/* ========== */
/* = Global = */
/* ========== */
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px;
@ -5,10 +9,31 @@ body {
margin: 0;
}
.NB-hidden {
display: none;
}
.NB-left {
float: left;
}
.NB-right {
float: right;
}
.NB-raquo {
font-size: 18px;
vertical-align: baseline;
line-height: 12px;
}
/* ========== */
/* = Layout = */
/* ========== */
.NB-page {
background: #EBC55F url(/media/img/reader/background-control-light.png) repeat 0 0;
padding: 0 12px;
}
/* ========== */
/* = Header = */
/* ========== */
@ -16,7 +41,6 @@ body {
header {
padding: 64px 14px;
text-shadow: 1px 1px 0 #E0E0E0;
background-color: #EBC55F;
overflow: hidden;
}
@ -128,6 +152,19 @@ header {
.NB-title .NB-title-left {
overflow: hidden;
}
/* ========= */
/* = Story = */
/* ========= */
.NB-mark {
margin: 0 auto 36px;
max-width: 800px;
border-top-left-radius: 6px;
border-top-right-radius: 6px;
overflow: hidden;
}
/* ===================== */
/* = Story Feed Header = */
/* ===================== */
@ -201,7 +238,24 @@ header {
#E9EAEF 10%,
#F5F8FA 84%
);
border-top: 1px solid #C0C0C0;
}
.NB-story-header-wrapper {
border-bottom: 1px solid #D0D0D0;
border-right: 1px solid #909090;
border-left: 1px solid #909090;
}
@media all and (max-width: 800px) {
.NB-story-header {
padding-right: 100px;
}
}
@media all and (max-width: 600px) {
.NB-story-header {
padding-right: 12px;
}
}
.NB-story-title {
@ -264,30 +318,370 @@ header {
font-size: 7px;
}
.NB-story-header .NB-story-modifications-button {
width: 16px;
height: 16px;
background: transparent url('/media/embed/reader/code_icon.png') no-repeat 0 0;
float: left;
margin: 3px 12px 0px 0px;
opacity: .6;
cursor: pointer;
}
.NB-story-header .NB-story-modifications-button:hover {
opacity: 1;
}
/* =========== */
/* = Stories = */
/* =========== */
.NB-divider {
border-top: 1px solid #F6F6F6;
border-bottom: 1px solid #E8E8E8;
/* border-top: 1px solid #F6F6F6;*/
/* border-bottom: 1px solid #E8E8E8;*/
height: 0;
}
.NB-story {
margin: 0 28px;
padding: 12px 212px 24px 0;
max-width: 700px;
background-color: #FAFAFA;
padding: 0 28px;
max-width: 800px;
border-right: 1px solid #909090;
border-bottom: 1px solid #909090;
border-left: 1px solid #909090;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
}
.NB-story a {
.NB-story-content {
padding: 12px 200px 24px 0;
}
@media all and (max-width: 800px) {
.NB-story-content {
padding-right: 100px;
}
}
@media all and (max-width: 700px) {
.NB-story-content {
padding-right: 50px;
}
}
@media all and (max-width: 600px) {
.NB-story-content {
padding-right: 0px;
}
}
.NB-story-content a {
color: #1F4499;
}
.NB-story a:hover {
.NB-story-content a:hover {
color: #99481D;
}
.NB-story img {
.NB-story-content img {
max-width: 100%;
height: auto;
}
.NB-story-content ins {
text-decoration: none;
color: inherit;
}
.NB-story-content del {
display: none;
}
.NB-pref-show-changes .NB-story-content ins {
text-decoration: underline;
color: #27452D;
}
.NB-pref-show-changes .NB-story-content del {
display: block;
color: #661616;
}
/* ======================= */
/* = Shares and Comments = */
/* ======================= */
.NB-story-comments a {
text-decoration: none;
}
.NB-story-comments a:hover {
color: #99481D;
}
.NB-story-comments a img {
border: none;
}
.NB-story-comments {
margin: 0 0 32px 0;
padding: 1px 0 0;
max-width: 800px;
border-top: 2px solid #353535;
border-bottom: 1px solid #353535;
font-family: "Lucida Sans", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;
}
.NB-story-comment {
border-top: 1px solid #A6A6A6;
background-color: #FCFCFC;
position: relative;
padding: 0 12px 2px 54px;
line-height: 20px;
overflow: hidden;
}
.NB-story-comment .NB-user-avatar {
position: absolute;
left: 6px;
top: 6px;
cursor: pointer;
}
.NB-story-comment .NB-user-avatar.NB-story-comment-reshare {
top: 22px;
left: 6px;
z-index: 1;
}
.NB-story-comment .NB-user-avatar img {
border-radius: 6px;
margin: 2px 0 0 1px;
width: 38px;
height: 38px;
}
.NB-story-comment .NB-user-avatar.NB-story-comment-reshare img {
height: 24px;
width: 24px;
}
.NB-story-comment .NB-story-comment-author-container {
overflow: hidden;
margin: 6px 0 0;
}
.NB-story-comment .NB-story-comment-reshares {
position: absolute;
top: 0;
left: 8px;
z-index: 0;
}
.NB-story-comment .NB-story-comment-reshares .NB-user-avatar {
top: 8px;
left: 12px;
}
.NB-story-comment .NB-story-comment-reshares .NB-user-avatar img {
width: 22px;
height: 22px;
border-radius: 3px;
}
.NB-story-comment .NB-story-comment-username {
float: left;
font-size: 11px;
color: #1D4BA6;
font-weight: bold;
margin: 0 10px 0 0;
text-shadow: 0 -1px 0 #F0F0F0;
cursor: pointer;
}
.NB-story-comment .NB-story-comment-date {
text-transform: uppercase;
font-size: 10px;
color: #9D9D9D;
font-weight: bold;
float: left;
}
.NB-story-comment .NB-story-comment-content {
float: left;
color: #303030;
}
.NB-story-comment .NB-story-comment-reply-button {
padding: 4px 24px 4px 12px;
float: left;
cursor: pointer;
}
.NB-story-comment .NB-story-comment-reply-button .NB-story-comment-reply-button-wrapper {
text-transform: uppercase;
background-color: #E9AF86;
color: white;
padding: 1px 4px;
line-height: 9px;
font-size: 9px;
}
.NB-story-comment .NB-story-comment-reply-button:hover .NB-story-comment-reply-button-wrapper {
background-color: #DE772B;
}
.NB-story-comment .NB-story-comment-reply-button:active .NB-story-comment-reply-button-wrapper {
background-color: #9F3A00;
}
.NB-story-comment-reply {
border-top: 1px solid #E0E0E0;
padding: 4px 0;
overflow: hidden;
clear: both;
position: relative;
padding: 6px 0 6px 32px;
line-height: 18px;
}
.NB-story-comment-reply .NB-story-comment-reply-photo {
width: 24px;
height: 24px;
border-radius: 3px;
position: absolute;
left: 0px;
top: 10px;
cursor: pointer;
}
.NB-story-comment-edit-button {
padding: 4px 24px 4px 12px;
float: left;
cursor: pointer;
}
.NB-story-comment-edit-button .NB-story-comment-edit-button-wrapper {
text-transform: uppercase;
background-color: #74A2E7;
color: white;
padding: 1px 4px;
line-height: 9px;
font-size: 9px;
}
.NB-story-comment-edit-button:hover .NB-story-comment-edit-button-wrapper {
background-color: #5073BC;
}
.NB-story-comment-edit-button:active .NB-story-comment-edit-button-wrapper {
background-color: #2A3B72;
}
.NB-story-comment-share-edit-button {
padding-right: 0;
}
.NB-story-comment-reply-content {
clear: both;
color: #303030;
float: left;
}
.NB-story-comment-reply-form {
padding-top: 11px;
}
.NB-story-comment-reply-form .NB-story-comment-reply-username {
margin: 1px 8px 6px 0;
}
.NB-story-comment-reply-form .NB-story-comment-reply-comments {
margin: 0 8px 4px 0;
width: 62%;
display: block;
float: left;
font-size: 12px;
}
.NB-story-comment-reply-form .NB-modal-submit-button {
float: left;
font-size: 10px;
padding: 2px 8px;
line-height: 16px;
margin: 0;
}
.NB-story-comment-reply-form .NB-error {
font-size: 10px;
color: #6A1000;
padding: 4px 0 0;
line-height: 14px;
font-weight: bold;
clear: both;
}
.NB-story-comments-public-teaser-wrapper,
.NB-story-comments-public-header-wrapper {
border-top: 1px solid #353535;
padding: 1px 0;
cursor: pointer;
}
.NB-story-comments-public-header-wrapper {
cursor: default;
}
.NB-story-comments-public-teaser,
.NB-story-comments-public-header {
background-color: #B1B6B4;
color: white;
text-shadow: 0 1px 0 #505050;
font-weight: bold;
text-transform: uppercase;
font-size: 10px;
padding: 2px 12px;
overflow: hidden;
-webkit-transition: all .12s ease-out;
-moz-transition: all .12s ease-out;
-o-transition: all .12s ease-out;
-ms-transition: all .12s ease-out;
}
.NB-story-comments-public-header {
background-color: whiteSmoke;
color: #404040;
text-shadow: 0 1px 0 white;
}
.NB-story-comments-shares-teaser-wrapper {
border-top: 0;
padding-top: 0;
}
.NB-story-comments-shares-teaser {
background-color: whiteSmoke;
color: #202020;
cursor: default;
text-shadow: 0 1px 0 #FFF;
font-weight: bold;
text-transform: uppercase;
font-size: 10px;
padding: 8px 12px 0px;
overflow: hidden;
height: 27px;
-webkit-transition: all .12s ease-out;
-moz-transition: all .12s ease-out;
-o-transition: all .12s ease-out;
-ms-transition: all .12s ease-out;
}
.NB-story-comments-public-teaser-wrapper:hover .NB-story-comments-public-teaser {
background-color: #2B478C;
background-image: none;
}
.NB-story-comments-public-teaser b {
padding: 0 1px;
font-size: 12px;
}
.NB-story-share-label {
display: inline-block;
margin: 0 4px 0 0;
}
.NB-story-share-profiles {
display: inline-block;
vertical-align: top;
height: 24px;
padding-top: 2px;
margin-top: -4px;
overflow: hidden;
}
.NB-story-share-profiles.NB-story-share-profiles-public {
float: right;
}
.NB-story-share-profiles.NB-story-share-profiles-public .NB-story-share-profile {
float: right;
}
.NB-story-share-profiles {
display: inline-block;
}
.NB-story-share-profiles .NB-user-avatar {
float: left;
font-size: 0;
vertical-align: middle;
height: 22px;
width: 22px;
margin: 0 4px 0 0;
cursor: pointer;
}
.NB-story-share-profiles .NB-user-avatar img {
width: 22px;
height: 22px;
border-radius: 3px;
}
.NB-story-share-profiles .NB-user-username {
float: left;
}
.NB-story-comment .NB-story-comment-content {
clear: both;
padding: 0 0 6px 0;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
media/img/reader/diigo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
media/img/reader/kippt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -19,6 +19,7 @@ NEWSBLUR.AssetModel = Backbone.Router.extend({
this.friends = {};
this.profile = {};
this.user_profile = new NEWSBLUR.Models.User();
this.social_services = {};
this.user_profiles = new NEWSBLUR.Collections.Users();
this.follower_profiles = new NEWSBLUR.Collections.Users();
this.following_profiles = new NEWSBLUR.Collections.Users();
@ -216,7 +217,8 @@ NEWSBLUR.AssetModel = Backbone.Router.extend({
}, callback);
},
mark_story_as_shared: function(story_id, feed_id, comments, source_user_id, callback, error_callback) {
mark_story_as_shared: function(story_id, feed_id, comments, source_user_id, post_to_services,
callback, error_callback) {
var pre_callback = _.bind(function(data) {
if (data.user_profiles) {
this.add_user_profiles(data.user_profiles);
@ -231,7 +233,28 @@ NEWSBLUR.AssetModel = Backbone.Router.extend({
story_id: story_id,
feed_id: feed_id,
comments: comments,
source_user_id: source_user_id
source_user_id: source_user_id,
post_to_services: post_to_services
}, pre_callback, error_callback);
} else {
error_callback();
}
},
mark_story_as_unshared: function(story_id, feed_id, callback, error_callback) {
var pre_callback = _.bind(function(data) {
if (data.user_profiles) {
this.add_user_profiles(data.user_profiles);
}
var story = this.get_story(story_id);
story.set(data.story);
callback(data);
}, this);
if (NEWSBLUR.Globals.is_authenticated) {
this.make_request('/social/unshare_story', {
story_id: story_id,
feed_id: feed_id
}, pre_callback, error_callback);
} else {
error_callback();
@ -272,6 +295,7 @@ NEWSBLUR.AssetModel = Backbone.Router.extend({
self.starred_count = subscriptions.starred_count;
self.social_feeds.reset(subscriptions.social_feeds);
self.user_profile.set(subscriptions.social_profile);
self.social_services = subscriptions.social_services;
if (!_.isEqual(self.favicons, {})) {
self.feeds.each(function(feed) {
@ -431,7 +455,18 @@ NEWSBLUR.AssetModel = Backbone.Router.extend({
if (first_load || !page) this.read_stories_river_count = 0;
var pre_callback = function(data) {
return self.load_feed_precallback(data, feed_id, callback, first_load);
self.load_feed_precallback(data, feed_id, callback, first_load);
if (NEWSBLUR.reader.flags['non_premium_river_view']) {
var visible_stories = self.stories.visible().length;
var max_stories = NEWSBLUR.reader.constants.RIVER_STORIES_FOR_STANDARD_ACCOUNT;
console.log(["checking no more stories", visible_stories, max_stories]);
if (visible_stories >= max_stories) {
self.flags['no_more_stories'] = true;
self.stories.trigger('no_more_stories');
}
}
};
this.feed_id = feed_id;
@ -606,6 +641,12 @@ NEWSBLUR.AssetModel = Backbone.Router.extend({
return counts;
},
unfetched_feeds: function() {
return this.feeds.filter(function(feed) {
return feed.get('active') && !feed.get('fetched_once') && !feed.get('has_exception');
});
},
set_feed: function(feed_id, feed) {
if (!feed) {
feed = feed_id;

View file

@ -25,9 +25,8 @@ NEWSBLUR.Models.Feed = Backbone.Model.extend({
delete_feed: function(options) {
options = options || {};
var view = options.view || this.get_view();
console.log(["Delete Feed", this, view, view.collection && view.collection.options.title]);
NEWSBLUR.assets.delete_feed(this.id, view.collection && view.collection.options.title);
NEWSBLUR.assets.delete_feed(this.id, view.options.folder_title);
view.delete_feed();
},

View file

@ -174,7 +174,6 @@ NEWSBLUR.Collections.Folders = Backbone.Collection.extend({
comparator: function(modelA, modelB) {
var sort_order = NEWSBLUR.assets.preference('feed_order');
if (modelA.is_feed() != modelB.is_feed()) {
// Feeds above folders
return modelA.is_feed() ? -1 : 1;
@ -187,6 +186,11 @@ NEWSBLUR.Collections.Folders = Backbone.Collection.extend({
var feedA = modelA.feed;
var feedB = modelB.feed;
if (!feedA || !feedB) {
// console.log(["missing feed", feedA, feedB, modelA, modelB]);
return !feedA ? 1 : -1;
}
if (sort_order == 'ALPHABETICAL' || !sort_order) {
return feedA.get('feed_title').toLowerCase() > feedB.get('feed_title').toLowerCase() ? 1 : -1;
} else if (sort_order == 'MOSTUSED') {

View file

@ -1,6 +1,7 @@
NEWSBLUR.Models.Story = Backbone.Model.extend({
initialize: function() {
this.bind('change:selected', this.change_selected);
this.bind('change:comments', this.populate_comments);
this.bind('change:comment_count', this.populate_comments);
this.populate_comments();
@ -44,6 +45,10 @@ NEWSBLUR.Models.Story = Backbone.Model.extend({
mark_read: function(options) {
return NEWSBLUR.assets.stories.mark_read(this, options);
},
change_selected: function(model, selected, changes) {
model.collection.detect_selected_story(model, selected);
}
});
@ -59,7 +64,7 @@ NEWSBLUR.Collections.Stories = Backbone.Collection.extend({
active_story: null,
initialize: function() {
this.bind('change:selected', this.detect_selected_story, this);
// this.bind('change:selected', this.detect_selected_story, this);
this.bind('reset', this.clear_previous_stories_stack, this);
},
@ -86,11 +91,10 @@ NEWSBLUR.Collections.Stories = Backbone.Collection.extend({
return;
}
this.last_read_story_id = story.id;
clearTimeout(this.read_story_delay);
this.read_story_delay = _.delay(_.bind(function() {
if (delay || this.last_read_story_id == story.id || delay == 0) {
if (!delay || (delay && this.active_story.id == story.id)) {
var mark_read_fn = NEWSBLUR.assets.mark_story_as_read;
var feed = NEWSBLUR.assets.get_feed(NEWSBLUR.reader.active_feed);
if (!feed) {
@ -246,7 +250,6 @@ NEWSBLUR.Collections.Stories = Backbone.Collection.extend({
get_next_unread_story: function(options) {
options = options || {};
var visible_stories = this.visible_and_unread(options.score, true);
console.log(["visible and unread", visible_stories, this, options.score]);
if (!visible_stories.length) return;
if (!this.active_story) {

View file

@ -37,7 +37,6 @@
$feedbar: $('.NB-feedbar')
};
this.flags = {
'feed_view_images_loaded': {},
'bouncing_callout': false,
'has_unfetched_feeds': false,
'count_unreads_after_import_working': false,
@ -51,7 +50,6 @@
'fetched_feeds': 0,
'page_fill_outs': 0,
'recommended_feed_page': 0,
'feed_view_positions_timer': 0,
'interactions_page': 1,
'activities_page': 1
};
@ -61,7 +59,6 @@
'iframe_story_positions_keys': [],
'feed_view_story_positions_keys': [],
'river_feeds_with_unreads': [],
'mouse_position_y': parseInt(this.model.preference('lock_mouse_indicator'), 10),
'$feed_in_social_feed_list': {}
};
this.views = {};
@ -69,7 +66,7 @@
this.constants = {
FEED_REFRESH_INTERVAL: (1000 * 60) * 1, // 1 minute
FILL_OUT_PAGES: 50,
RIVER_STORIES_FOR_STANDARD_ACCOUNT: 12
RIVER_STORIES_FOR_STANDARD_ACCOUNT: 5
};
// ==================
@ -120,6 +117,7 @@
this.setup_howitworks_hovers();
this.setup_interactions_module();
this.setup_activities_module();
this.setup_unfetched_feed_check();
},
// ========
@ -149,18 +147,20 @@
flag = 'story';
}
this.counts['feed_view_positions_timer'] = 0;
this.flags.scrolling_by_selecting_story_title = true;
clearTimeout(this.locks.scrolling);
this.locks.scrolling = _.delay(_.bind(function() {
this.flags.scrolling_by_selecting_story_title = false;
}, this), 1000);
this.make_content_pane_feed_counter();
this.switch_taskbar_view(view, flag);
NEWSBLUR.app.story_titles.fill_out();
// this.flags.fetch_story_locations_in_feed_view = this.flags.fetch_story_locations_in_feed_view ||
// _.throttle(_.bind(this.fetch_story_locations_in_feed_view, this), 2000);
// this.flags.fetch_story_locations_in_feed_view();
this.flags.fetch_story_locations_in_feed_view = this.flags.fetch_story_locations_in_feed_view ||
_.throttle(function() {
NEWSBLUR.app.story_list.reset_story_positions();
}, 2000);
this.flags.fetch_story_locations_in_feed_view();
},
apply_resizable_layout: function(refresh) {
@ -439,8 +439,14 @@
}
},
blur_to_page: function() {
$(':focus').blur();
blur_to_page: function(options) {
options = options || {};
if (options.manage_menu) {
$('.NB-menu-manage :focus').blur();
} else {
$(':focus').blur();
}
},
// ==============
@ -625,10 +631,16 @@
dir = '-';
}
// NEWSBLUR.log(['page_in_story', this.$s.$story_pane, direction, page_height, scroll_height]);
if (this.story_view == 'page') {
this.$s.$feed_iframe.scrollTo({top:dir+'='+scroll_height, left:'+=0'}, 230, {queue: false});
} else if (this.story_view == 'feed') {
this.$s.$feed_stories.scrollTo({top:dir+'='+scroll_height, left:'+=0'}, 230, {queue: false});
if (this.story_view == 'page' && !this.flags['page_view_showing_feed_view']) {
this.$s.$feed_iframe.scrollTo({
top: dir+'='+scroll_height,
left:'+=0'
}, 230, {queue: false});
} else if (this.story_view == 'feed' || this.flags['page_view_showing_feed_view']) {
this.$s.$feed_stories.scrollTo({
top: dir+'='+scroll_height,
left:'+=0'
}, 230, {queue: false});
}
},
@ -859,11 +871,6 @@
reset_feed: function() {
$.extend(this.flags, {
'iframe_story_locations_fetched': false,
'iframe_view_loaded': false,
'iframe_view_not_busting': false,
'feed_view_images_loaded': {},
'feed_view_positions_calculated': false,
'scrolling_by_selecting_story_title': false,
'page_view_showing_feed_view': false,
'feed_view_showing_story_view': false,
@ -886,11 +893,9 @@
'iframe_story_positions_keys': [],
'feed_view_story_positions_keys': [],
'river_feeds_with_unreads': [],
'mouse_position_y': parseInt(this.model.preference('lock_mouse_indicator'), 10),
'prefetch_last_story': 0,
'prefetch_iteration': 0,
'feed_title_floater_story_id': null,
'last_read_story_id': null,
'$feed_in_social_feed_list': {}
});
@ -918,9 +923,7 @@
$('.task_view_page', this.$s.$taskbar).removeClass('NB-disabled');
$('.task_view_story', this.$s.$taskbar).removeClass('NB-disabled');
$('.task_view_page', this.$s.$taskbar).removeClass('NB-task-return');
// $('.feed_counts_floater').remove();
clearTimeout(this.flags['next_fetch']);
this.counts['feed_view_positions_timer'] = 0;
if (this.flags['showing_feed_in_tryfeed_view'] || this.flags['showing_social_feed_in_tryfeed_view']) {
this.hide_tryfeed_view();
@ -1017,15 +1020,9 @@
// NEWSBLUR.log(['post_open_feed', data.stories, this.flags]);
this.flags['opening_feed'] = false;
this.flags['feed_view_positions_calculated'] = false;
this.counts['feed_view_positions_timer'] = 0;
// this.create_story_titles(stories);
// this.make_story_feed_entries(stories, first_load);
this.find_story_with_action_preference_on_open_feed();
this.show_feed_hidden_story_title_indicator(true);
this.show_story_titles_above_intelligence_level({'animate': false});
// this.scroll_story_titles_to_show_selected_story_title();
// this.fill_out_story_titles();
if (this.counts['find_next_unread_on_page_of_feed_stories_load']) {
this.show_next_unread_story(true);
} else if (this.counts['find_last_unread_on_page_of_feed_stories_load']) {
@ -1034,27 +1031,9 @@
this.select_story_in_feed();
}
this.flags['story_titles_loaded'] = true;
if (!first_load) {
var stories_count = this.cache['iframe_story_positions_keys'].length;
this.flags['iframe_story_locations_fetched'] = false;
NEWSBLUR.app.original_tab_view.fetch_story_locations_in_story_frame();
if (this.story_view == 'feed' || this.flags['page_view_showing_feed_view']) {
// this.prefetch_story_locations_in_feed_view();
}
} else {
if (this.story_view == 'page') {
if (this.flags['iframe_view_loaded']) {
NEWSBLUR.log(['Titles loaded, iframe loaded']);
NEWSBLUR.app.original_tab_view.fetch_story_locations_in_story_frame();
} else {
NEWSBLUR.log(['Titles loaded, iframe NOT loaded -- prefetching now']);
_.delay(_.bind(function() {
NEWSBLUR.app.original_tab_view.prefetch_story_locations_in_story_frame();
}, this), 250);
}
} else if (this.story_view == 'feed') {
// this.prefetch_story_locations_in_feed_view();
} else if (this.story_view == 'story' && !this.counts['find_next_unread_on_page_of_feed_stories_load']) {
if (first_load) {
if (this.story_view == 'story' &&
!this.counts['find_next_unread_on_page_of_feed_stories_load']) {
this.show_next_story(1);
}
@ -1070,6 +1049,7 @@
set_correct_story_view_for_feed: function(feed_id, view) {
var feed = this.model.get_feed(feed_id);
var $original_tabs = $('.task_view_page, .task_view_story');
var $page_tab = $('.task_view_page');
view = view || this.model.view_setting(feed_id);
if (feed && feed.get('disabled_page')) {
@ -1098,7 +1078,11 @@
$(this).tipsy('disable');
});
}
if (feed_id == 'starred') {
$page_tab.addClass('NB-disabled-page').addClass('NB-disabled');
}
this.story_view = view;
},
@ -1139,7 +1123,7 @@
this.flags.river_view = true;
$('.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);
@ -1154,19 +1138,12 @@
if (this.active_feed == 'starred') {
// NEWSBLUR.log(['post_open_starred_stories', data.stories.length, first_load]);
this.flags['opening_feed'] = false;
this.flags['feed_view_positions_calculated'] = false;
this.counts['feed_view_positions_timer'] = 0;
// this.create_story_titles(data.stories, {'river_stories': true});
// this.make_story_feed_entries(data.stories, first_load, {'river_stories': true});
this.find_story_with_action_preference_on_open_feed();
if (this.counts['select_story_in_feed'] || this.flags['select_story_in_feed']) {
this.select_story_in_feed();
}
this.show_story_titles_above_intelligence_level({'animate': false});
this.flags['story_titles_loaded'] = true;
// this.prefetch_story_locations_in_feed_view();
// this.scroll_story_titles_to_show_selected_story_title();
// this.fill_out_story_titles();
}
},
@ -1229,10 +1206,6 @@
this.flags['non_premium_river_view'] = true;
}
this.flags['opening_feed'] = false;
this.flags['feed_view_positions_calculated'] = false;
this.counts['feed_view_positions_timer'] = 0;
// this.create_story_titles(data.stories, {'river_stories': true});
// this.make_story_feed_entries(data.stories, first_load, {'river_stories': true});
this.show_feed_hidden_story_title_indicator(true);
this.find_story_with_action_preference_on_open_feed();
this.show_story_titles_above_intelligence_level({'animate': false});
@ -1244,9 +1217,6 @@
} else if (this.counts['select_story_in_feed'] || this.flags['select_story_in_feed']) {
this.select_story_in_feed();
}
// this.scroll_story_titles_to_show_selected_story_title();
// this.fill_out_story_titles();
// this.prefetch_story_locations_in_feed_view();
this.hide_stories_progress_bar();
}
},
@ -1306,13 +1276,14 @@
this.iframe_scroll = null;
this.flags['opening_feed'] = true;
feed.set('selected', true);
feed.set('selected', true, options);
this.make_feed_title_in_stories(feed.id);
this.$s.$body.addClass('NB-view-river');
this.flags.social_view = true;
var explicit_view_setting = this.model.view_setting(this.active_feed);
this.set_correct_story_view_for_feed(this.active_feed, explicit_view_setting);
this.set_correct_story_view_for_feed(this.active_feed);
// TODO: Only make feed the default for blurblogs, not overriding an explicit pref.
this.switch_taskbar_view('feed');
this.setup_mousemove_on_views();
@ -1323,18 +1294,24 @@
if (this.story_view == 'page') {
_.delay(_.bind(function() {
if (!options.delay || feed_id == this.next_feed) {
if (!options.delay || feed.id == this.next_feed) {
NEWSBLUR.app.original_tab_view.load_feed_iframe();
}
}, this), options.delay || 0);
} else {
this.flags['iframe_prevented_from_loading'] = true;
}
if (!options.silent && feed.get('feed_title')) {
var slug = _.string.words(_.string.clean(feed.get('feed_title').replace(/[^a-z0-9\. ]/ig, ''))).join('-').toLowerCase();
var url = "social/" + feed.get('user_id') + "/" + slug;
if (!_.string.include(window.location.pathname, url)) {
var params = {};
if (_.string.include(window.location.pathname, "social/" + feed.get('user_id'))) {
params['replace'] = true;
}
// console.log(["Navigating to social", url, window.location.pathname]);
NEWSBLUR.router.navigate(url);
NEWSBLUR.router.navigate(url, params);
}
} else if (!feed.get('feed_title')) {
console.log(["No feed title on social", feed]);
@ -1350,10 +1327,6 @@
if (this.active_feed && NEWSBLUR.utils.is_feed_social(this.active_feed)) {
this.flags['opening_feed'] = false;
this.flags['feed_view_positions_calculated'] = false;
this.counts['feed_view_positions_timer'] = 0;
// this.create_story_titles(data.stories, {'river_stories': true});
// this.make_story_feed_entries(data.stories, first_load, {'river_stories': true});
this.find_story_with_action_preference_on_open_feed();
this.show_story_titles_above_intelligence_level({'animate': false});
this.show_feed_hidden_story_title_indicator(true);
@ -1365,9 +1338,6 @@
} else if (this.counts['find_last_unread_on_page_of_feed_stories_load']) {
this.show_last_unread_story(true);
}
// this.scroll_story_titles_to_show_selected_story_title();
// this.fill_out_story_titles();
// this.prefetch_story_locations_in_feed_view();
this.hide_stories_progress_bar();
if (this.flags['showing_social_feed_in_tryfeed_view']) {
@ -1443,7 +1413,7 @@
console.log(["show_stories_error", arguments]);
this.hide_stories_progress_bar();
this.flags['iframe_view_not_busting'] = true;
NEWSBLUR.app.original_tab_view.iframe_not_busting();
this.model.flags['no_more_stories'] = true;
var $error = $.make('div', { className: 'NB-feed-error' }, [
@ -1478,36 +1448,6 @@
// = Story Pane - All Views =
// ==========================
// open_story: function(story, $story_title, options) {
// var self = this;
// var feed_position;
// var iframe_position;
// options = options || {};
// // NEWSBLUR.log(['open_story', this.story_view, story, options]);
//
// if (this.active_story != story || options.story_id) {
// if (this.story_view == 'page') {
// var $iframe_story = this.find_story_in_feed_iframe(story);
// if (!$iframe_story || !$iframe_story.length || !this.flags['story_titles_loaded']) {
// // If the iframe has not yet loaded, we can't touch it.
// // So just assume story not found.
// this.switch_to_correct_view(false);
// feed_position = this.scroll_to_story_in_story_feed(story, $feed_story, options);
// NEWSBLUR.app.story_list.show_stories_preference_in_feed_view(true);
// } else {
// iframe_position = this.scroll_to_story_in_iframe(story, $iframe_story);
// this.switch_to_correct_view(iframe_position);
// }
// } else if (this.story_view == 'feed') {
// this.switch_to_correct_view();
// feed_position = this.scroll_to_story_in_story_feed(story, $feed_story, options);
// NEWSBLUR.app.story_list.show_stories_preference_in_feed_view(true);
// } else if (this.story_view == 'story') {
// this.open_story_in_story_view(story);
// }
// }
// },
switch_to_correct_view: function(found_story_in_page) {
// NEWSBLUR.log(['Found story', this.story_view, found_story_in_page, this.flags['page_view_showing_feed_view'], this.flags['feed_view_showing_story_view']]);
if (found_story_in_page === false) {
@ -1704,6 +1644,54 @@
NEWSBLUR.assets.stories.mark_read(story, {skip_delay: true});
},
send_story_to_diigo: function(story_id) {
var story = this.model.get_story(story_id);
var url = 'http://www.diigo.com/post?';
var url = [
url,
'url=',
encodeURIComponent(story.get('story_permalink')),
'&title=',
encodeURIComponent(story.get('story_title')),
'&tags=',
encodeURIComponent(story.get('story_tags').join(', '))
].join('');
window.open(url, '_blank');
NEWSBLUR.assets.stories.mark_read(story, {skip_delay: true});
},
send_story_to_kippt: function(story_id) {
var story = this.model.get_story(story_id);
var url = 'https://kippt.com/extensions/new/?';
var url = [
url,
'url=',
encodeURIComponent(story.get('story_permalink')),
'&title=',
encodeURIComponent(story.get('story_title')),
'&tags=',
encodeURIComponent(story.get('story_tags').join(', '))
].join('');
window.open(url, '_blank');
NEWSBLUR.assets.stories.mark_read(story, {skip_delay: true});
},
send_story_to_evernote: function(story_id) {
var story = this.model.get_story(story_id);
var url = 'https://www.evernote.com/clip.action?';
var url = [
url,
'url=',
encodeURIComponent(story.get('story_permalink')),
'&title=',
encodeURIComponent(story.get('story_title')),
'&tags=',
encodeURIComponent(story.get('story_tags').join(', '))
].join('');
window.open(url, '_blank');
NEWSBLUR.assets.stories.mark_read(story, {skip_delay: true});
},
send_story_to_googleplus: function(story_id) {
var story = this.model.get_story(story_id);
var url = 'https://plusone.google.com/_/+1/confirm'; //?hl=en&url=${url}
@ -2005,53 +1993,6 @@
// = Story Pane - Feed View =
// ==========================
make_story_feed_entries: function(stories, first_load, options) {
var $feed_view = this.$s.$feed_view;
var $stories = this.$s.$feed_stories;
var self = this;
var unread_view = this.get_unread_view_score();
var river_same_feed;
var feed = this.model.get_feed(this.active_feed);
options = options || {};
if (first_load && !options.refresh_load) {
$('.NB-feed-story-endbar', $feed_view).remove();
}
for (var s in stories) {
// REMOVED
var image_count = $('img', $story).length;
if (!image_count) {
this.flags.feed_view_images_loaded[story.id] = true;
} else {
// Progressively load the images in each story, so that when one story
// loads, the position is calculated and the next story can calculate
// its position (atfer its own images are loaded).
this.flags.feed_view_images_loaded[story.id] = false;
(function($story, story, image_count) {
$('img', $story).load(function() {
// NEWSBLUR.log(['Loaded image', $story, story, image_count]);
if (image_count == 1) {
self.flags.feed_view_images_loaded[story.id] = true;
} else {
image_count--;
}
return true;
});
})($story, story, image_count);
}
}
if (!stories || !stories.length) {
// this.fetch_story_locations_in_feed_view({'reset_timer': true});
}
if (first_load) NEWSBLUR.app.story_list.show_stories_preference_in_feed_view(true);
},
apply_story_styling: function(reset_stories) {
var $body = this.$s.$body;
$body.removeClass('NB-theme-sans-serif');
@ -2118,6 +2059,7 @@
self.flags.scrolling_by_selecting_story_title = false;
}, 550);
if (view == 'page') {
console.log(["iframe_prevented_from_loading", this.flags['iframe_prevented_from_loading']]);
if (this.flags['iframe_prevented_from_loading']) {
NEWSBLUR.app.original_tab_view.load_feed_iframe();
}
@ -2142,9 +2084,7 @@
'queue': false
});
if (!this.flags['feed_view_positions_calculated']) {
// this.prefetch_story_locations_in_feed_view();
}
NEWSBLUR.app.story_list.reset_story_positions();
} else if (view == 'story') {
$story_pane.animate({
'left': -2 * $feed_iframe.width()
@ -2452,11 +2392,11 @@
$.make('div', { className: 'NB-menu-manage-title' }, 'Intelligence trainer'),
$.make('div', { className: 'NB-menu-manage-subtitle' }, 'What you like and dislike.')
]),
$.make('li', { className: 'NB-menu-separator' }),
$.make('li', { className: 'NB-menu-manage-feed NB-menu-manage-delete NB-menu-manage-socialfeed-delete' }, [
(feed.get('user_id') != NEWSBLUR.Globals.user_id && $.make('li', { className: 'NB-menu-separator' })),
(feed.get('user_id') != NEWSBLUR.Globals.user_id && $.make('li', { className: 'NB-menu-manage-feed NB-menu-manage-delete NB-menu-manage-socialfeed-delete' }, [
$.make('div', { className: 'NB-menu-manage-image' }),
$.make('div', { className: 'NB-menu-manage-title' }, 'Unfollow')
]),
])),
$.make('li', { className: 'NB-menu-manage-feed NB-menu-manage-delete-confirm NB-menu-manage-socialfeed-delete-confirm' }, [
$.make('div', { className: 'NB-menu-manage-image' }),
$.make('div', { className: 'NB-menu-manage-title' }, 'Really unfollow?')
@ -2519,7 +2459,7 @@
var starred_class = story.get('starred') ? ' NB-story-starred ' : '';
var starred_title = story.get('starred') ? 'Remove bookmark' : 'Save This Story';
var shared_class = story.get('shared') ? ' NB-story-shared ' : '';
var shared_title = story.get('shared') ? 'Shared' : 'Share story';
var shared_title = story.get('shared') ? 'Shared' : 'Post to blurblog';
story.story_share_menu_view = new NEWSBLUR.Views.StoryShareView({
model: story
});
@ -2571,6 +2511,21 @@
}, this)).bind('mouseleave', _.bind(function(e) {
$(e.target).siblings('.NB-menu-manage-title').text('Email story').parent().removeClass('NB-menu-manage-highlight-pinboard');
}, this))),
(NEWSBLUR.Preferences['story_share_diigo'] && $.make('div', { className: 'NB-menu-manage-thirdparty-icon NB-menu-manage-thirdparty-diigo'}).bind('mouseenter', _.bind(function(e) {
$(e.target).siblings('.NB-menu-manage-title').text('Diigo').parent().addClass('NB-menu-manage-highlight-diigo');
}, this)).bind('mouseleave', _.bind(function(e) {
$(e.target).siblings('.NB-menu-manage-title').text('Email story').parent().removeClass('NB-menu-manage-highlight-diigo');
}, this))),
(NEWSBLUR.Preferences['story_share_kippt'] && $.make('div', { className: 'NB-menu-manage-thirdparty-icon NB-menu-manage-thirdparty-kippt'}).bind('mouseenter', _.bind(function(e) {
$(e.target).siblings('.NB-menu-manage-title').text('Kippt').parent().addClass('NB-menu-manage-highlight-kippt');
}, this)).bind('mouseleave', _.bind(function(e) {
$(e.target).siblings('.NB-menu-manage-title').text('Email story').parent().removeClass('NB-menu-manage-highlight-kippt');
}, this))),
(NEWSBLUR.Preferences['story_share_evernote'] && $.make('div', { className: 'NB-menu-manage-thirdparty-icon NB-menu-manage-thirdparty-evernote'}).bind('mouseenter', _.bind(function(e) {
$(e.target).siblings('.NB-menu-manage-title').text('Evernote').parent().addClass('NB-menu-manage-highlight-evernote');
}, this)).bind('mouseleave', _.bind(function(e) {
$(e.target).siblings('.NB-menu-manage-title').text('Email story').parent().removeClass('NB-menu-manage-highlight-evernote');
}, this))),
(NEWSBLUR.Preferences['story_share_googleplus'] && $.make('div', { className: 'NB-menu-manage-thirdparty-icon NB-menu-manage-thirdparty-googleplus'}).bind('mouseenter', _.bind(function(e) {
$(e.target).siblings('.NB-menu-manage-title').text('Google+').parent().addClass('NB-menu-manage-highlight-googleplus');
}, this)).bind('mouseleave', _.bind(function(e) {
@ -2606,6 +2561,12 @@
this.send_story_to_readability(story.id);
} else if ($target.hasClass('NB-menu-manage-thirdparty-pinboard')) {
this.send_story_to_pinboard(story.id);
} else if ($target.hasClass('NB-menu-manage-thirdparty-diigo')) {
this.send_story_to_diigo(story.id);
} else if ($target.hasClass('NB-menu-manage-thirdparty-kippt')) {
this.send_story_to_kippt(story.id);
} else if ($target.hasClass('NB-menu-manage-thirdparty-evernote')) {
this.send_story_to_evernote(story.id);
} else if ($target.hasClass('NB-menu-manage-thirdparty-googleplus')) {
this.send_story_to_googleplus(story.id);
} else if ($target.hasClass('NB-menu-manage-thirdparty-instapaper')) {
@ -2794,7 +2755,7 @@
});
// Hide menu on esc.
$('input,textarea', $manage_menu_container).bind('keydown', 'esc', function(e) {
$('input,textarea', $manage_menu_container).bind('keydown.manage_menu', 'esc', function(e) {
e.preventDefault();
self.flags['showing_confirm_input_on_manage_menu'] = false;
self.hide_manage_menu(type, $item, true);
@ -2857,7 +2818,7 @@
}
$('.NB-task-manage').removeClass('NB-hover');
this.blur_to_page();
this.blur_to_page({manage_menu: true});
},
// ========================
@ -3132,7 +3093,7 @@
var $confirm = $('.NB-menu-manage-story-share-confirm');
$share.removeClass('NB-menu-manage-story-share-cancel');
var text = 'Share story';
var text = 'Post to blurblog';
if (shared) {
text = 'Shared';
$share.addClass('NB-active');
@ -3167,7 +3128,6 @@
this.model.preference('unread_view', value);
}
this.flags['unread_threshold_temporarily'] = null;
this.flags['feed_view_positions_calculated'] = false;
this.switch_feed_view_unread_view(value);
this.show_feed_hidden_story_title_indicator(true);
this.show_story_titles_above_intelligence_level({'animate': true, 'follow': true});
@ -3323,26 +3283,35 @@
}
}
if ((this.story_view == 'feed' || this.flags['page_view_showing_feed_view']) &&
NEWSBLUR.assets.preference('feed_view_single_story')) {
// No need to show/hide feed view stories under single_story preference.
// If the user switches to feed/page, then no animation is happening
// and this will work anyway.
var active_story = this.active_story;
var $active_story = this.active_story && this.active_story.story_view.$el;
if ($active_story && $active_story.length || true) {
$stories_show = $stories_show.not('.NB-feed-story');
$stories_hide = $stories_hide.not('.NB-feed-story');
}
console.log(["single story", $stories_show.length, $stories_hide.length, this.active_story, active_story && active_story.id]);
}
if (!options['animate']) {
$stories_hide.css({'display': 'none'});
$stories_show.css({'display': 'block'});
NEWSBLUR.app.story_titles.fill_out();
}
if (this.story_view == 'feed' && !this.model.preference('feed_view_single_story')) {
if ($stories_show.filter(':visible').length != $stories_show.length
|| $stories_hide.filter(':visible').length != 0) {
NEWSBLUR.log(['Show/Hide stories', $stories_show.filter(':visible').length, $stories_show.length, $stories_hide.filter(':visible').length, $stories_hide.length]);
setTimeout(function() {
self.flags['feed_view_positions_calculated'] = false;
// self.prefetch_story_locations_in_feed_view();
}, 500);
}
if (!NEWSBLUR.assets.preference('feed_view_single_story')) {
_.delay(function() {
NEWSBLUR.app.story_list.reset_story_positions();
}, 500);
}
if (options['animate'] && options['follow'] &&
($stories_hide.length || $stories_show.length)) {
// NEWSBLUR.log(['Showing correct stories', this.story_view, this.flags['feed_view_positions_calculated'], unread_view_name, $stories_show.length, $stories_hide.length]);
// NEWSBLUR.log(['Showing correct stories', this.story_view, unread_view_name, $stories_show.length, $stories_hide.length]);
if (this.model.preference('animations')) {
$stories_hide.slideUp(500, function() {
NEWSBLUR.app.story_titles.fill_out();
@ -3390,7 +3359,7 @@
// this.socket.refresh_feeds = _.debounce(_.bind(this.force_feeds_refresh, this), 1000*10);
this.socket.on('connect', _.bind(function() {
var active_feeds = this.send_socket_active_feeds();
console.log(["Connected to real-time pubsub with " + active_feeds.length + " feeds."]);
// console.log(["Connected to real-time pubsub with " + active_feeds.length + " feeds."]);
this.socket.on('feed:update', _.bind(function(feed_id, message) {
console.log(['Real-time feed update', feed_id, message]);
this.force_feeds_refresh(false, false, feed_id);
@ -3474,7 +3443,7 @@
});
}
}, refresh_interval);
console.log(["Setting refresh interval to every " + refresh_interval/1000 + " seconds."]);
// console.log(["Setting refresh interval to every " + refresh_interval/1000 + " seconds."]);
},
force_feed_refresh: function(feed_id, new_feed_id) {
@ -3614,6 +3583,7 @@
position = position - 8; // Compensate for mouse indicator height.
}
this.$s.$mouse_indicator.css('top', position);
this.cache.mouse_position_y = position;
},
// ==========================
@ -4048,7 +4018,7 @@
follow_user_in_tryfeed: function(feed_id) {
var self = this;
var socialsub = this.model.get_feed(feed_id);
this.model.follow_user(socialsub.user_id, function(data) {
this.model.follow_user(socialsub.get('user_id'), function(data) {
NEWSBLUR.app.feed_list.make_social_feeds();
self.open_social_stories(feed_id);
});
@ -4159,6 +4129,21 @@
}, $.noop);
},
// ===================
// = Unfetched Feeds =
// ===================
setup_unfetched_feed_check: function() {
this.locks.unfetched_feed_check = setInterval(_.bind(function() {
var unfetched_feeds = NEWSBLUR.assets.unfetched_feeds();
if (unfetched_feeds.length) {
_.each(unfetched_feeds, _.bind(function(feed) {
this.force_instafetch_stories(feed.id);
}, this));
}
}, this), 60*10*1000);
},
// ==========
// = Events =
// ==========
@ -4997,6 +4982,13 @@
e.preventDefault();
self.open_next_unread_story_across_feeds();
});
$document.bind('keydown', 'c', function(e) {
e.preventDefault();
NEWSBLUR.app.story_list.scroll_to_selected_story(self.active_story, {
scroll_to_comments: true,
scroll_offset: -50
});
});
$document.bind('keydown', 'm', function(e) {
e.preventDefault();
self.show_last_unread_story();
@ -5058,6 +5050,7 @@
});
$document.bind('keydown', 'u', function(e) {
e.preventDefault();
if (!self.active_story) return;
var story_id = self.active_story.id;
var story = self.model.get_story(story_id);
if (self.active_story && !self.active_story.get('read_status')) {

View file

@ -461,7 +461,7 @@ _.extend(NEWSBLUR.ReaderIntro.prototype, {
self.advance_import_carousel(0);
$loading.removeClass('NB-active');
NEWSBLUR.log(['Error', data, status, e]);
$error.text("There was a problem uploading your OPML file. Try e-mailing it to samuel@ofbrooklyn.com.");
$error.text("There was a problem uploading your OPML file. Try e-mailing it to samuel@newsblur.com.");
$error.slideDown(300);
},
data: formData,

View file

@ -206,7 +206,7 @@ NEWSBLUR.ReaderKeyboard.prototype = {
]),
$.make('div', { className: 'NB-keyboard-group' }, [
$.make('div', { className: 'NB-keyboard-shortcut' }, [
$.make('div', { className: 'NB-keyboard-shortcut-explanation' }, 'Share story'),
$.make('div', { className: 'NB-keyboard-shortcut-explanation' }, 'Post to blurblog'),
$.make('div', { className: 'NB-keyboard-shortcut-key' }, [
'shift',
$.make('span', '+'),

View file

@ -493,6 +493,18 @@ _.extend(NEWSBLUR.ReaderPreferences.prototype, {
$.make('input', { type: 'checkbox', id: 'NB-preference-story-share-pinboard', name: 'story_share_pinboard' }),
$.make('label', { 'for': 'NB-preference-story-share-pinboard' })
]),
$.make('div', { className: 'NB-preference-option', title: 'Diigo' }, [
$.make('input', { type: 'checkbox', id: 'NB-preference-story-share-diigo', name: 'story_share_diigo' }),
$.make('label', { 'for': 'NB-preference-story-share-diigo' })
]),
$.make('div', { className: 'NB-preference-option', title: 'Kippt' }, [
$.make('input', { type: 'checkbox', id: 'NB-preference-story-share-kippt', name: 'story_share_kippt' }),
$.make('label', { 'for': 'NB-preference-story-share-kippt' })
]),
$.make('div', { className: 'NB-preference-option', title: 'Evernote' }, [
$.make('input', { type: 'checkbox', id: 'NB-preference-story-share-evernote', name: 'story_share_evernote' }),
$.make('label', { 'for': 'NB-preference-story-share-evernote' })
]),
$.make('div', { className: 'NB-preference-option', title: 'Google+' }, [
$.make('input', { type: 'checkbox', id: 'NB-preference-story-share-googleplus', name: 'story_share_googleplus' }),
$.make('label', { 'for': 'NB-preference-story-share-googleplus' })

View file

@ -7,7 +7,7 @@ NEWSBLUR.ReaderSendEmail = function(story_id, options) {
this.model = NEWSBLUR.assets;
this.story_id = story_id;
this.story = this.model.get_story(story_id);
this.feed_id = this.story.story_feed_id;
this.feed_id = this.story.get('story_feed_id');
this.feed = this.model.get_feed(this.feed_id);
this.runner();

View file

@ -51,7 +51,7 @@ _.extend(NEWSBLUR.ReaderSocialProfile.prototype, {
]),
$.make('td', [
$.make('fieldset', [
$.make('legend', 'People you also follow'),
$.make('legend', 'People you know'),
$.make('div', { className: 'NB-modal-section NB-profile-following-youknow' })
]),
$.make('fieldset', [
@ -71,7 +71,7 @@ _.extend(NEWSBLUR.ReaderSocialProfile.prototype, {
]),
$.make('td', [
$.make('fieldset', [
$.make('legend', 'People you follow'),
$.make('legend', 'People you know'),
$.make('div', { className: 'NB-modal-section NB-profile-followers-youknow' })
]),
$.make('fieldset', [
@ -146,9 +146,8 @@ _.extend(NEWSBLUR.ReaderSocialProfile.prototype, {
return $.make('div', { className: 'NB-profile-link', title: user.get('username') }, [
$.make('img', { src: user.get('photo_url') })
]).tipsy({
delayIn: 50,
gravity: 's',
fade: true,
delayIn: 1,
offset: 3
}).data('user_id', user_id);
}));

View file

@ -17,6 +17,41 @@ NEWSBLUR.utils = {
return score;
},
generate_shadow: _.memoize(function(feed) {
if (!feed) return '';
var color = feed.get('favicon_color');
if (!color) {
return '0 1px 0 #222';
}
var r = parseInt(color.substr(0, 2), 16);
var g = parseInt(color.substr(2, 2), 16);
var b = parseInt(color.substr(4, 2), 16);
if (feed.is_light()) {
return [
'0 1px 0 ',
'rgb(',
[r+35, g+35, b+35].join(','),
')'
].join('');
} else {
return [
'0 1px 0 ',
'rgb(',
[
parseInt(r*(6/8), 10),
parseInt(g*(6/8), 10),
parseInt(b*(6/8), 10)
].join(','),
')'
].join('');
}
}, function(feed) {
return "" + feed.id;
}),
generate_gradient: _.memoize(function(feed, type) {
if (!feed) return '';
var color = feed.get('favicon_color');
@ -26,7 +61,7 @@ NEWSBLUR.utils = {
var g = parseInt(color.substr(2, 2), 16);
var b = parseInt(color.substr(4, 2), 16);
if (type == 'border' || (type == 'shadow' && !feed.is_light())) {
if (type == 'border') {
return [
(type == 'border' ? '1px solid ' : '') + 'rgb(',
[
@ -36,12 +71,6 @@ NEWSBLUR.utils = {
].join(','),
')'
].join('');
} else if (type == 'shadow') {
return [
'rgb(',
[r+35, g+35, b+35].join(','),
')'
].join('');
} else if (type == 'webkit') {
return [
'-webkit-gradient(',

View file

@ -52,7 +52,7 @@ NEWSBLUR.About.prototype = {
]),
$.make('li', [
'E-mail: ',
$.make('a', { href: 'mailto:samuel@ofbrooklyn.com' }, 'samuel@ofbrooklyn.com')
$.make('a', { href: 'mailto:samuel@newsblur.com' }, 'samuel@newsblur.com')
]),
$.make('li', { className: 'last' }, [
'Made in: ',

View file

@ -118,7 +118,7 @@ NEWSBLUR.Faq.prototype = {
$.make('div', { className: 'NB-faq-question' }, 'Help! I have an issue and it\'s not mentioned here.'),
$.make('div', { className: 'NB-faq-answer last' }, [
'Please, please, please e-mail ',
$.make('a', { href: 'mailto:samuel@ofbrooklyn.com' }, 'samuel@ofbrooklyn.com'),
$.make('a', { href: 'mailto:samuel@newsblur.com' }, 'samuel@newsblur.com'),
'. If you have an issue it is entirely possible that other people do, too.'
])
])

View file

@ -181,7 +181,9 @@ NEWSBLUR.Views.FeedList = Backbone.View.extend({
});
}
if (!feed_view) {
feed_view = model.views[0];
feed_view = _.detect(model.views, _.bind(function(view) {
return !!view.$el.closest(this.$s.$feed_lists).length;
}, this));
}
if (feed_view) {
@ -195,7 +197,6 @@ NEWSBLUR.Views.FeedList = Backbone.View.extend({
// console.log(["scroll_to_show_selected_feed", feed_view, feed_view.$el, is_feed_visible]);
if (!is_feed_visible) {
var container_offset = $feed_lists.position().top;
var scroll = feed_view.$el.position().top;
var container = $feed_lists.scrollTop();
var height = $feed_lists.outerHeight();

View file

@ -137,9 +137,13 @@ NEWSBLUR.Views.FeedTitleView = Backbone.View.extend({
}
if (feed.is_social()) {
extra_classes += ' NB-feed-social';
if (feed.get('subscription_user_id') && !feed.get('shared_stories_count')) {
extra_classes += ' NB-feed-inactive';
}
if (feed.get('subscription_user_id') == NEWSBLUR.Globals.user_id) {
extra_classes += ' NB-feed-self-blurblog';
}
}
return extra_classes;

View file

@ -2,21 +2,32 @@ NEWSBLUR.Views.OriginalTabView = Backbone.View.extend({
initialize: function() {
_.bindAll(this, 'handle_scroll_feed_iframe', 'handle_mousemove_iframe_view', 'setup_events');
this.cache = {
iframe: {},
prefetch_iteration: 0,
iframe_fetching_story_locations: false,
iframe_story_locations_fetched: false
};
this.flags = {};
this.locks = {
iframe_buster_buster: false
};
this.reset_flags();
this.unload_feed_iframe();
this.setElement(NEWSBLUR.reader.$s.$feed_iframe);
this.setup_events();
this.collection.bind('change:selected', this.toggle_selected_story, this);
this.collection.bind('reset', this.reset_story_positions, this);
this.collection.bind('add', this.reset_story_positions, this);
},
reset_flags: function() {
this.cache = {
iframe: {},
prefetch_iteration: 0
};
this.flags = {
iframe_fetching_story_locations: false,
iframe_story_locations_fetched: false
};
this.counts = {
positions_timer: 0
};
this.locks = {
iframe_buster_buster: false
};
},
setup_events: function() {
@ -148,9 +159,9 @@ NEWSBLUR.Views.OriginalTabView = Backbone.View.extend({
if (!this.flags['iframe_view_not_busting']) {
var feed_id = this.active_feed;
var feed_id = NEWSBLUR.reader.active_feed;
_.delay(_.bind(function() {
if (feed_id == this.active_feed) {
if (feed_id == NEWSBLUR.reader.active_feed) {
this.flags['iframe_view_not_busting'] = true;
}
}, this), 200);
@ -212,16 +223,15 @@ NEWSBLUR.Views.OriginalTabView = Backbone.View.extend({
var stories = NEWSBLUR.assets.stories;
var $iframe = this.$el.contents();
var prefetch_tries_left = 3;
this.cache['prefetch_iteration'] += 1;
NEWSBLUR.log(['Prefetching', !this.flags['iframe_fetching_story_locations'], !this.flags['iframe_story_locations_fetched']]);
if (!this.flags['iframe_loaded']) return;
this.cache['prefetch_iteration'] += 1;
NEWSBLUR.log(['Prefetching Original', !this.flags['iframe_fetching_story_locations'], !this.flags['iframe_story_locations_fetched']]);
if (!this.flags['iframe_fetching_story_locations']
&& !this.flags['iframe_story_locations_fetched']) {
// $iframe.unbind('scroll').scroll($.rescope(this.handle_scroll_feed_iframe, this));
// $iframe
// .unbind('mousemove.reader')
// .bind('mousemove.reader', $.rescope(this.handle_mousemove_iframe_view, this));
this.setup_events();
var last_story_index = this.cache.iframe_story_positions_keys.length;
var last_story_position = _.last(this.cache.iframe_story_positions_keys);
var last_story = this.cache.iframe_story_positions[last_story_position];
@ -229,7 +239,7 @@ NEWSBLUR.Views.OriginalTabView = Backbone.View.extend({
if (last_story) {
$last_story = this.find_story_in_feed_iframe(last_story, $iframe);
}
NEWSBLUR.log(['last_story', last_story_index, last_story_position, last_story, $last_story]);
// NEWSBLUR.log(['last_story', last_story_index, last_story_position, last_story, $last_story]);
var last_story_same_position;
if ($last_story && $last_story.length) {
last_story_same_position = parseInt($last_story.offset().top, 10)==last_story_position;
@ -246,7 +256,7 @@ NEWSBLUR.Views.OriginalTabView = Backbone.View.extend({
if (last_story_same_position && i < last_story_index) return true;
var $story = this.find_story_in_feed_iframe(story, $iframe);
NEWSBLUR.log(['Pre-fetching', i, last_story_index, last_story_same_position, $story, story.get('story_title')]);
// NEWSBLUR.log(['Pre-fetching', i, last_story_index, last_story_same_position, $story, story.get('story_title')]);
if (!$story ||
!$story.length ||
this.flags['iframe_fetching_story_locations'] ||
@ -273,12 +283,16 @@ NEWSBLUR.Views.OriginalTabView = Backbone.View.extend({
this.prefetch_story_locations_in_story_frame();
}
}, this), 1000);
} else {
this.fetch_story_locations_in_story_frame();
}
},
fetch_story_locations_in_story_frame: function($iframe) {
fetch_story_locations_in_story_frame: function($iframe, options) {
var self = this;
options = options || {};
if (!$iframe) $iframe = this.$el.contents();
if (options.reset_timer) this.counts['positions_timer'] = 0;
this.flags['iframe_fetching_story_locations'] = true;
this.flags['iframe_story_locations_fetched'] = false;
@ -291,36 +305,64 @@ NEWSBLUR.Views.OriginalTabView = Backbone.View.extend({
});
NEWSBLUR.assets.stories.any(_.bind(function(story, i) {
if (story['story_feed_id'] == this.active_feed ||
"social:" + story['social_user_id'] == this.active_feed) {
if (story.get('story_feed_id') == NEWSBLUR.reader.active_feed ||
"social:" + story.get('social_user_id') == NEWSBLUR.reader.active_feed) {
var $story = this.find_story_in_feed_iframe(story, $iframe);
// NEWSBLUR.log(['Fetching story', i, story.get('story_title'), $story]);
if (self.cache['story_misses'] > 5) {
NEWSBLUR.log(['iFrame view entirely loaded', self.cache['story_misses'], self.cache.iframe_stories]);
// NEWSBLUR.log(['iFrame view entirely loaded', self.cache['story_misses'], self.cache.iframe_stories]);
self.flags['iframe_story_locations_fetched'] = true;
self.flags['iframe_fetching_story_locations'] = false;
clearInterval(self.flags['iframe_scroll_snapback_check']);
return true;
}
} else if (story && story['story_feed_id'] != this.active_feed &&
"social:" + story['social_user_id'] != this.active_feed) {
NEWSBLUR.log(['Switched off iframe early', this.active_feed, story['story_feed_id'], story['social_user_id']]);
} else if (story && story.get('story_feed_id') != NEWSBLUR.reader.active_feed &&
"social:" + story.get('social_user_id') != NEWSBLUR.reader.active_feed) {
NEWSBLUR.log(['Switched off iframe early', NEWSBLUR.reader.active_feed, story.get('story_feed_id'), story.get('social_user_id')]);
return true;
}
}, this));
NEWSBLUR.log(['Original view entirely loaded', _.keys(self.cache.iframe_stories).length + " stories", this.counts['positions_timer']/1000 + " sec delay"]);
this.counts['positions_timer'] = Math.max(this.counts['positions_timer']*2, 1000);
clearTimeout(this.flags['next_fetch']);
this.flags['next_fetch'] = _.delay(_.bind(this.fetch_story_locations_in_story_frame, this),
this.counts['positions_timer']);
},
reset_story_positions: function(models) {
if (!models || !models.length) {
models = NEWSBLUR.assets.stories;
}
if (!models.length) return;
this.flags['iframe_fetching_story_locations'] = false;
this.flags['iframe_story_locations_fetched'] = false;
if (NEWSBLUR.reader.flags['story_titles_loaded']) {
NEWSBLUR.log(['iframe loaded, titles loaded (early)']);
this.fetch_story_locations_in_story_frame();
} else {
this.prefetch_story_locations_in_story_frame();
}
},
// ===========
// = Actions =
// ===========
iframe_not_busting: function() {
this.flags['iframe_not_busting'] = true;
},
unload_feed_iframe: function() {
var $taskbar_view_page = $('.NB-taskbar .task_view_page');
$taskbar_view_page.removeClass('NB-task-return');
// REMOVE ME, this disabled the iframe buster buster
// NEWSBLUR.reader.flags['iframe_view_loaded'] = false;
clearInterval(this.flags['iframe_scroll_snapback_check']);
this.flags['iframe_story_locations_fetched'] = false;
NEWSBLUR.reader.flags['iframe_prevented_from_loading'] = false;
@ -332,7 +374,11 @@ NEWSBLUR.Views.OriginalTabView = Backbone.View.extend({
});
$.extend(this.flags, {
'iframe_scroll_snapback_check': false
'iframe_loaded': false,
'iframe_scroll_snapback_check': false,
'iframe_view_not_busting': false,
'iframe_fetching_story_locations': false,
'iframe_story_locations_fetched': false
});
this.$el.removeAttr('src');
@ -347,6 +393,8 @@ NEWSBLUR.Views.OriginalTabView = Backbone.View.extend({
this.unload_feed_iframe();
this.flags['iframe_loaded'] = true;
var page_url = '/reader/page/'+feed_id;
if (NEWSBLUR.reader.flags['social_view']) {
var feed = NEWSBLUR.assets.get_feed(feed_id);
@ -360,27 +408,26 @@ NEWSBLUR.Views.OriginalTabView = Backbone.View.extend({
this.$el.attr('src', page_url);
this.enable_iframe_buster_buster();
if (NEWSBLUR.reader.flags['iframe_view_loaded']) {
NEWSBLUR.log(['Titles loaded, iframe loaded']);
var $iframe = this.$el.contents();
this.fetch_story_locations_in_story_frame($iframe);
} else {
NEWSBLUR.log(['Titles loaded, iframe NOT loaded -- prefetching now']);
_.delay(_.bind(function() {
this.prefetch_story_locations_in_story_frame();
}, this), 500);
}
_.delay(_.bind(function() {
this.prefetch_story_locations_in_story_frame();
}, this), 500);
this.setup_events();
this.$el.ready(function() {
if (feed_id != NEWSBLUR.reader.active_feed) {
console.log(["Switched feed, unloading iframe"]);
self.unload_feed_iframe();
return;
}
setTimeout(function() {
self.$el.load(function() {
self.return_to_snapback_position(true);
});
}, 50);
self.flags['iframe_scroll_snapback_check'] = setInterval(function() {
// NEWSBLUR.log(['Checking scroll', self.iframe_scroll, self.flags.iframe_scroll_snap_back_prepared]);
// NEWSBLUR.log(['Checking scroll', self.iframe_scroll, self.flags.iframe_scroll_snap_back_prepared, self.flags['iframe_scroll_snapback_check']]);
if (self.iframe_scroll && self.flags.iframe_scroll_snap_back_prepared) {
self.return_to_snapback_position();
} else {
@ -430,18 +477,17 @@ NEWSBLUR.Views.OriginalTabView = Backbone.View.extend({
setup_feed_page_iframe_load: function() {
this.$el.load(_.bind(function() {
NEWSBLUR.reader.flags['iframe_view_loaded'] = true;
this.disable_iframe_buster_buster();
this.setup_events();
if (NEWSBLUR.reader.flags['story_titles_loaded']) {
NEWSBLUR.log(['iframe loaded, titles loaded']);
this.fetch_story_locations_in_story_frame($iframe_contents);
this.fetch_story_locations_in_story_frame();
}
// try {
var $iframe_contents = this.$el.contents();
$iframe_contents.find('a')
.unbind('click.NB-taskbar')
.bind('click.NB-taskbar', function(e) {
.bind('click.NB-taskbar', _.bind(function(e) {
var href = $(this).attr('href');
if (href.indexOf('#') == 0) {
e.preventDefault();
@ -458,7 +504,7 @@ NEWSBLUR.Views.OriginalTabView = Backbone.View.extend({
return false;
}
this.taskbar_show_return_to_page();
});
}, this));
// } catch(e) {
// // Not on local domain. Ignore.
// }
@ -505,10 +551,10 @@ NEWSBLUR.Views.OriginalTabView = Backbone.View.extend({
this.locks.iframe_buster_buster = setInterval(function() {
if (prevent_bust > 0) {
prevent_bust -= 2;
if (!self.flags['iframe_view_loaded'] &&
if (self.flags['iframe_story_locations_fetched'] &&
!self.flags['iframe_view_not_busting'] &&
_.contains(['page', 'story'], self.story_view) &&
self.active_feed) {
NEWSBLUR.reader.active_feed) {
$('.NB-feed-frame').attr('src', '');
window.top.location = '/reader/buster';
$('.task_view_feed').click();
@ -592,7 +638,8 @@ NEWSBLUR.Views.OriginalTabView = Backbone.View.extend({
toggle_selected_story: function(model, selected, options) {
if (selected &&
NEWSBLUR.reader.story_view == 'page' &&
!options.selected_in_original) {
!options.selected_in_original &&
!options.selected_by_scrolling) {
var found = this.scroll_to_selected_story(model);
NEWSBLUR.reader.switch_to_correct_view(found);
if (!found) {

View file

@ -98,7 +98,7 @@ NEWSBLUR.Views.SocialProfileBadge = Backbone.View.extend({
follow_user: function() {
this.$('.NB-loading').addClass('NB-active');
NEWSBLUR.reader.model.follow_user(this.model.get('user_id'), _.bind(function(data, follow_user) {
NEWSBLUR.assets.follow_user(this.model.get('user_id'), _.bind(function(data, follow_user) {
this.$('.NB-loading').removeClass('NB-active');
this.model.set(follow_user);

View file

@ -25,7 +25,7 @@ NEWSBLUR.Views.StoryComment = Backbone.View.extend({
]),
$.make('div', { className: 'NB-story-comment-author-container' }, [
(this.model.get('source_user_id') && $.make('div', { className: 'NB-story-comment-reshares' }, [
NEWSBLUR.Views.ProfileThumb.create(this.model.get('source_user_id'))
NEWSBLUR.Views.ProfileThumb.create(this.model.get('source_user_id')).render().el
])),
$.make('div', { className: 'NB-story-comment-username' }, this.user.get('username')),
$.make('div', { className: 'NB-story-comment-date' }, this.model.get('shared_date') + ' ago'),
@ -101,7 +101,8 @@ NEWSBLUR.Views.StoryComment = Backbone.View.extend({
this.remove_social_comment_reply_form();
}, this));
$('input', $form).focus();
// this.fetch_story_locations_in_feed_view();
NEWSBLUR.app.story_list.fetch_story_locations_in_feed_view();
},
remove_social_comment_reply_form: function() {
@ -118,7 +119,7 @@ NEWSBLUR.Views.StoryComment = Backbone.View.extend({
if (!comment_reply || comment_reply.length <= 1) {
this.remove_social_comment_reply_form();
// this.fetch_story_locations_in_feed_view();
NEWSBLUR.app.story_list.fetch_story_locations_in_feed_view();
return;
}
@ -133,7 +134,7 @@ NEWSBLUR.Views.StoryComment = Backbone.View.extend({
_.bind(function(data) {
this.model.set(data.comment);
this.render();
// this.fetch_story_locations_in_feed_view();
NEWSBLUR.app.story_list.fetch_story_locations_in_feed_view();
}, this), _.bind(function(data) {
var message = data && data.message || "Sorry, this reply could not be posted. Probably a bug.";
if (!NEWSBLUR.Globals.is_authenticated) {
@ -143,7 +144,7 @@ NEWSBLUR.Views.StoryComment = Backbone.View.extend({
$submit.removeClass('NB-disabled').text('Post');
$form.find('.NB-error').remove();
$form.append($error);
// this.fetch_story_locations_in_feed_view();
NEWSBLUR.app.story_list.fetch_story_locations_in_feed_view();
}, this));
}

View file

@ -111,7 +111,7 @@ NEWSBLUR.Views.StoryCommentsView = Backbone.View.extend({
}, this));
this.$('.NB-story-comments-public-teaser-wrapper').replaceWith($comments);
// this.fetch_story_locations_in_feed_view();
NEWSBLUR.app.story_list.fetch_story_locations_in_feed_view();
}, this));
}

View file

@ -28,8 +28,8 @@ NEWSBLUR.Views.StoryDetailView = Backbone.View.extend({
this.model.bind('change:intelligence', this.render_header, this);
// Binding directly instead of using event delegation. Need for speed.
this.$el.bind('mouseenter', this.mouseenter);
this.$el.bind('mouseleave', this.mouseleave);
// this.$el.bind('mouseenter', this.mouseenter);
// this.$el.bind('mouseleave', this.mouseleave);
if (!this.options.feed_floater) {
this.model.story_view = this;
@ -47,7 +47,8 @@ NEWSBLUR.Views.StoryDetailView = Backbone.View.extend({
model: this.model,
el: this.el
}).template({
story: this.model
story: this.model,
social_services: NEWSBLUR.assets.social_services
});
this.$el.html(this.template(params));
this.toggle_classes();
@ -73,8 +74,8 @@ NEWSBLUR.Views.StoryDetailView = Backbone.View.extend({
feed : NEWSBLUR.reader.flags.river_view && this.feed,
tag : _.first(this.model.get("story_tags")),
title : this.make_story_title(),
authors_score : this.classifiers.authors[this.model.get('story_authors')],
tags_score : this.classifiers.tags,
authors_score : this.classifiers && this.classifiers.authors[this.model.get('story_authors')],
tags_score : this.classifiers && this.classifiers.tags,
options : this.options
};
},
@ -98,7 +99,7 @@ NEWSBLUR.Views.StoryDetailView = Backbone.View.extend({
<% if (story.get("story_tags", []).length) { %>\
<div class="NB-feed-story-tags">\
<% _.each(story.get("story_tags"), function(tag) { %>\
<div class="NB-feed-story-tag <% if (tags_score[tag]) { %>NB-score-<%= tags_score[tag] %><% } %>">\
<div class="NB-feed-story-tag <% if (tags_score && tags_score[tag]) { %>NB-score-<%= tags_score[tag] %><% } %>">\
<%= tag %>\
</div>\
<% }) %>\
@ -119,6 +120,9 @@ NEWSBLUR.Views.StoryDetailView = Backbone.View.extend({
<% } %>\
<%= story.get("long_parsed_date") %>\
</span>\
<% if (story.get("starred_date")) { %>\
<span class="NB-feed-story-starred-date"><%= story.get("starred_date") %></span>\
<% } %>\
<% } %>\
</div>\
</div>\
@ -141,7 +145,7 @@ NEWSBLUR.Views.StoryDetailView = Backbone.View.extend({
</div>\
<div class="NB-sideoption NB-feed-story-share">\
<div class="NB-sideoption-icon">&nbsp;</div>\
<div class="NB-sideoption-title"><%= story.get("shared") ? "Shared" : "Share this story" %></div>\
<div class="NB-sideoption-title"><%= story.get("shared") ? "Shared" : "Post to Blurblog" %></div>\
</div>\
<%= story_share_view %>\
</div>\
@ -156,7 +160,7 @@ NEWSBLUR.Views.StoryDetailView = Backbone.View.extend({
$header.css('background-image', NEWSBLUR.utils.generate_gradient(this.feed, 'moz'));
$header.css('borderTop', NEWSBLUR.utils.generate_gradient(this.feed, 'border'));
$header.css('borderBottom', NEWSBLUR.utils.generate_gradient(this.feed, 'border'));
$header.css('textShadow', '0 1px 0 ' + NEWSBLUR.utils.generate_gradient(this.feed, 'shadow'));
$header.css('textShadow', NEWSBLUR.utils.generate_shadow(this.feed));
},
make_story_title: function() {
@ -174,9 +178,12 @@ NEWSBLUR.Views.StoryDetailView = Backbone.View.extend({
},
render_comments: function() {
var $original_comments = this.$('.NB-feed-story-share-container,.NB-feed-story-comments');
if (this.model.get("comment_count") || this.model.get("share_count")) {
var $comments = new NEWSBLUR.Views.StoryCommentsView({model: this.model}).render().el;
this.$('.NB-feed-story-share-container,.NB-feed-story-comments').replaceWith($comments);
$original_comments.replaceWith($comments);
} else if ($original_comments.length) {
$original_comments.replaceWith($.make('div', { className: 'NB-feed-story-share-container' }));
}
},
@ -246,6 +253,7 @@ NEWSBLUR.Views.StoryDetailView = Backbone.View.extend({
(NEWSBLUR.reader.story_view == 'feed' ||
(NEWSBLUR.reader.story_view == 'page' &&
NEWSBLUR.reader.flags['page_view_showing_feed_view']))) {
NEWSBLUR.app.story_list.show_stories_preference_in_feed_view();
NEWSBLUR.app.story_list.scroll_to_selected_story(model, options);
}
},
@ -348,10 +356,6 @@ NEWSBLUR.Views.StoryDetailView = Backbone.View.extend({
return false;
},
mark_story_as_shared: function() {
NEWSBLUR.reader.mark_story_as_shared(this.model.id, {'source': 'sideoption'});
},
hide_story_changes: function() {
var $button = this.$('.NB-feed-story-hide-changes');
@ -364,6 +368,7 @@ NEWSBLUR.Views.StoryDetailView = Backbone.View.extend({
}
$button.css('opacity', 1).fadeOut(400);
$button.tipsy('hide').tipsy('disable');
NEWSBLUR.app.story_list.fetch_story_locations_in_feed_view();
},
open_feed: function() {

View file

@ -5,20 +5,29 @@ NEWSBLUR.Views.StoryListView = Backbone.View.extend({
initialize: function() {
this.collection.bind('reset', this.reset_flags, this);
this.collection.bind('reset', this.render, this);
this.collection.bind('reset', this.reset_story_positions, this);
this.collection.bind('add', this.add, this);
this.collection.bind('change:selected', this.show_correct_feed_in_feed_title_floater, this);
this.collection.bind('add', this.reset_story_positions, this);
this.collection.bind('no_more_stories', this.show_no_more_stories, this);
this.$el.bind('mousemove', _.bind(this.handle_mousemove_feed_view, this));
// this.$el.scroll(_.bind(this.handle_scroll_feed_view, this));
this.$el.scroll(_.bind(this.handle_scroll_feed_view, this));
this.reset_flags();
},
reset_flags: function() {
this.cache = {
story_pane_position: null,
feed_title_floater_feed_id: null
feed_title_floater_feed_id: null,
feed_view_story_positions: {},
feed_view_story_positions_keys: []
};
this.flags = {
feed_view_images_loaded: {},
mousemove_timeout: false
};
this.counts = {
positions_timer: 0
};
},
// ==========
@ -59,17 +68,16 @@ NEWSBLUR.Views.StoryListView = Backbone.View.extend({
scroll_to_selected_story: function(story, options) {
options = options || {};
if (!story || !story.story_view) return;
if (!options.immediate) {
clearTimeout(NEWSBLUR.reader.locks.scrolling);
NEWSBLUR.reader.flags.scrolling_by_selecting_story_title = true;
}
var $story = story.story_view.$el;
if (NEWSBLUR.assets.preference('feed_view_single_story')) return;
if (!NEWSBLUR.assets.preference('animations')) options.immediate = true;
if (options.scroll_to_comments) {
$story = $('.NB-feed-story-comments', $story);
}
clearTimeout(NEWSBLUR.reader.locks.scrolling);
NEWSBLUR.reader.flags.scrolling_by_selecting_story_title = true;
this.$el.scrollable().stop();
this.$el.scrollTo($story, {
duration: options.immediate ? 0 : 340,
@ -78,8 +86,6 @@ NEWSBLUR.Views.StoryListView = Backbone.View.extend({
offset: options.scroll_offset || 0,
queue: false,
onAfter: function() {
if (options.immediate) return;
NEWSBLUR.reader.locks.scrolling = setTimeout(function() {
NEWSBLUR.reader.flags.scrolling_by_selecting_story_title = false;
}, 100);
@ -134,39 +140,42 @@ NEWSBLUR.Views.StoryListView = Backbone.View.extend({
},
show_stories_preference_in_feed_view: function(is_creating) {
var $feed_view = NEWSBLUR.reader.$s.$feed_view;
var $feed_view_stories = $(".NB-feed-story", $feed_view);
var $stories = NEWSBLUR.reader.$s.$feed_stories;
var story = NEWSBLUR.reader.active_story;
if (story && NEWSBLUR.assets.preference('feed_view_single_story')) {
// NEWSBLUR.log(['show_stories_preference_in_feed_view', is_creating, NEWSBLUR.assets.preference('feed_view_single_story'), $feed_view_stories.length + " stories"]);
$stories.removeClass('NB-feed-view-feed').addClass('NB-feed-view-story');
$feed_view_stories.css({'display': 'none'});
if (is_creating) this.$s.$feed_stories.scrollTop('0px');
var $current_story = this.get_current_story_from_story_titles($feed_view_stories);
if ($current_story && $current_story.length) {
$current_story.css({'display': 'block'});
}
if (NEWSBLUR.reader.active_story &&
NEWSBLUR.assets.preference('feed_view_single_story')) {
this.$el.removeClass('NB-feed-view-feed').addClass('NB-feed-view-story');
NEWSBLUR.reader.$s.$feed_stories.scrollTop('0px');
this.flags['feed_view_positions_calculated'] = false;
} else {
$stories.removeClass('NB-feed-view-story').addClass('NB-feed-view-feed');
if (!is_creating) {
NEWSBLUR.reader.show_story_titles_above_intelligence_level({'animate': false});
}
this.$el.removeClass('NB-feed-view-story').addClass('NB-feed-view-feed');
NEWSBLUR.reader.show_story_titles_above_intelligence_level({'animate': false});
}
NEWSBLUR.app.story_list.cache.story_pane_position = null;
this.cache.story_pane_position = null;
},
// =============
// = Positions =
// =============
is_feed_loaded_for_location_fetch: function() {
var images_begun = _.keys(this.flags.feed_view_images_loaded).length;
if (images_begun) {
var images_loaded = _.keys(this.flags.feed_view_images_loaded).length &&
_.all(_.values(this.flags.feed_view_images_loaded), _.identity);
return !!images_loaded;
}
return !!images_begun;
},
prefetch_story_locations_in_feed_view: function() {
var self = this;
var stories = NEWSBLUR.assets.stories;
// NEWSBLUR.log(['Prefetching', this.flags['feed_view_positions_calculated'], this.flags.feed_view_images_loaded, (_.keys(this.flags.feed_view_images_loaded).length > 0 || this.cache.feed_view_story_positions_keys.length > 0)]);
NEWSBLUR.log(['Prefetching Feed', this.flags['feed_view_positions_calculated'], this.flags.feed_view_images_loaded, (_.keys(this.flags.feed_view_images_loaded).length > 0 || this.cache.feed_view_story_positions_keys.length > 0), _.keys(this.flags.feed_view_images_loaded).length,
_.values(this.flags.feed_view_images_loaded), this.is_feed_loaded_for_location_fetch()]);
if (!NEWSBLUR.assets.stories.size()) return;
if (!this.flags['feed_view_positions_calculated']) {
$.extend(this.cache, {
@ -174,29 +183,25 @@ NEWSBLUR.Views.StoryListView = Backbone.View.extend({
'feed_view_story_positions_keys': []
});
for (var s in stories) {
var story = stories[s];
// var $story = self.cache.feed_view_stories[story.id];
// this.determine_feed_view_story_position($story, story);
// NEWSBLUR.log(['Pre-fetching', $story, story.get('story_title'), this.flags.feed_view_images_loaded[story.id]]);
// if (!$story || !$story.length || this.flags['feed_view_positions_calculated']) break;
}
}
if ((_.keys(this.flags.feed_view_images_loaded).length > 0 ||
this.cache.feed_view_story_positions_keys.length > 0) &&
(this.flags.feed_view_images_loaded.length &&
_.all(_.values(this.flags.feed_view_images_loaded)))) {
NEWSBLUR.assets.stories.any(_.bind(function(story) {
this.determine_feed_view_story_position(story);
var $story = story.story_view.$el;
if (!$story || !$story.length || this.flags['feed_view_positions_calculated']) {
return true;
}
}, this));
setTimeout(_.bind(function() {
if (!this.flags['feed_view_positions_calculated']) {
this.prefetch_story_locations_in_feed_view();
}
}, this), 2000);
}
if (this.is_feed_loaded_for_location_fetch()) {
this.fetch_story_locations_in_feed_view({'reset_timer': true});
} else {
// NEWSBLUR.log(['Still loading feed view...', _.keys(this.flags.feed_view_images_loaded).length, this.cache.feed_view_story_positions_keys.length, this.flags.feed_view_images_loaded]);
}
if (!this.flags['feed_view_positions_calculated']) {
setTimeout(function() {
if (!self.flags['feed_view_positions_calculated']) {
self.prefetch_story_locations_in_feed_view();
}
}, 2000);
NEWSBLUR.log(['Still loading feed view...', _.keys(this.flags.feed_view_images_loaded).length, this.cache.feed_view_story_positions_keys.length, this.flags.feed_view_images_loaded]);
}
},
@ -204,60 +209,95 @@ NEWSBLUR.Views.StoryListView = Backbone.View.extend({
options = options || {};
var stories = NEWSBLUR.assets.stories;
if (!stories || !stories.length) return;
if (options.reset_timer) this.counts['feed_view_positions_timer'] = 0;
if (options.reset_timer) this.counts['positions_timer'] = 0;
$.extend(this.cache, {
'feed_view_story_positions': {},
'feed_view_story_positions_keys': []
});
for (var s in stories) {
var story = stories[s];
var $story = this.cache.feed_view_stories[story.id];
this.determine_feed_view_story_position($story, story);
}
NEWSBLUR.assets.stories.each(_.bind(function(story) {
this.determine_feed_view_story_position(story);
}, this));
this.flags['feed_view_positions_calculated'] = true;
// NEWSBLUR.log(['Feed view entirely loaded', NEWSBLUR.assets.stories.length + " stories", this.counts['feed_view_positions_timer']/1000 + " sec delay"]);
// NEWSBLUR.log(['Feed view entirely loaded', NEWSBLUR.assets.stories.length + " stories", this.counts['positions_timer']/1000 + " sec delay"]);
this.counts['feed_view_positions_timer'] = Math.max(this.counts['feed_view_positions_timer']*2, 1000);
this.counts['positions_timer'] = Math.max(this.counts['positions_timer']*2, 1000);
clearTimeout(this.flags['next_fetch']);
this.flags['next_fetch'] = _.delay(_.bind(this.fetch_story_locations_in_feed_view, this),
this.counts['feed_view_positions_timer']);
this.counts['positions_timer']);
},
determine_feed_view_story_position: function($story, story) {
if ($story && $story.is(':visible')) {
determine_feed_view_story_position: function(story) {
var $story = story.story_view.$el;
if (story && $story.is(':visible')) {
var position_original = parseInt($story.position().top, 10);
var position_offset = parseInt($story.offsetParent().scrollTop(), 10);
var position = position_original + position_offset;
this.cache.feed_view_story_positions[position] = story;
this.cache.feed_view_story_positions_keys.push(position);
this.cache.feed_view_story_positions_keys.sort(function(a, b) { return a-b; });
// NEWSBLUR.log(['Positioning story', position, $story, story, this.cache.feed_view_story_positions_keys]);
// NEWSBLUR.log(['Positioning story', position, story.get('story_title')]);
}
},
check_feed_view_scrolled_to_bottom: function() {
var $story_titles = this.$s.$story_titles;
var $feed_view = this.$s.$feed_view;
if (!this.model.flags['no_more_stories']) {
console.log(["check_feed_view_scrolled_to_bottom"]);
var $last_story = $('.NB-feed-story', $feed_view).last();
var container_offset = $feed_view.position().top;
if (!NEWSBLUR.assets.flags['no_more_stories']) {
var last_story = NEWSBLUR.assets.stories.last();
var $last_story = last_story.story_view.$el;
var container_offset = this.$el.position().top;
var full_height = ($last_story.offset() && $last_story.offset().top) + $last_story.height() - container_offset;
var visible_height = $feed_view.height();
var scroll_y = $feed_view.scrollTop();
var visible_height = this.$el.height();
var scroll_y = this.$el.scrollTop();
// Fudge factor is simply because it looks better at 13 pixels off.
if ((visible_height + 26) >= full_height) {
// NEWSBLUR.log(['Feed view scroll', full_height, container_offset, visible_height, scroll_y]);
this.load_page_of_feed_stories();
NEWSBLUR.log(['Feed view scroll', full_height, container_offset, visible_height, scroll_y]);
NEWSBLUR.reader.load_page_of_feed_stories();
}
}
},
reset_story_positions: function(models) {
if (!models || !models.length) {
models = NEWSBLUR.assets.stories;
}
if (!models.length) return;
this.flags['feed_view_positions_calculated'] = false;
if (this.cache.story_pane_position == null) {
this.cache.story_pane_position = this.$el.offsetParent().offset().top;
}
models.each(_.bind(function(story) {
var image_count = story.story_view.$('.NB-feed-story-content img').length;
if (!image_count) {
// console.log(["No images", story.get('story_title')]);
this.flags.feed_view_images_loaded[story.id] = true;
} else if (!this.flags.feed_view_images_loaded[story.id]) {
// Progressively load the images in each story, so that when one story
// loads, the position is calculated and the next story can calculate
// its position (after its own images are loaded).
this.flags.feed_view_images_loaded[story.id] = false;
(function(story, image_count) {
story.story_view.$('.NB-feed-story-content img').load(function() {
// NEWSBLUR.log(['Loaded image', story.get('story_title'), image_count]);
if (image_count <= 1) {
NEWSBLUR.app.story_list.flags.feed_view_images_loaded[story.id] = true;
} else {
image_count--;
}
return true;
});
})(story, image_count);
}
}, this));
this.prefetch_story_locations_in_feed_view();
},
// ==========
// = Events =
// ==========
@ -276,63 +316,58 @@ NEWSBLUR.Views.StoryListView = Backbone.View.extend({
}
NEWSBLUR.reader.cache.mouse_position_y = e.pageY;
if (this.cache.story_pane_position == null) {
this.cache.story_pane_position = NEWSBLUR.reader.$s.$feed_stories.offsetParent().offset().top;
}
NEWSBLUR.reader.$s.$mouse_indicator.css('top', NEWSBLUR.reader.cache.mouse_position_y - this.cache.story_pane_position - 8);
if (this.flags['mousemove_timeout']) {
if (this.flags['mousemove_timeout'] ||
NEWSBLUR.reader.flags['scrolling_by_selecting_story_title']) {
return;
}
setTimeout(function() {
self.flags['mousemove_timeout'] = false;
}, 30);
// if (!this.flags['mousemove_timeout']
// && !this.flags['switching_to_feed_view']
// && !this.flags.scrolling_by_selecting_story_title
// && this.story_view != 'story') {
// var from_top = this.cache.mouse_position_y + this.$s.$feed_stories.scrollTop();
// var offset = this.cache.story_pane_position;
// var position = from_top - offset;
// var positions = this.cache.feed_view_story_positions_keys;
// var closest = $.closest(position, positions);
// var story = this.cache.feed_view_story_positions[positions[closest]];
// this.flags['mousemove_timeout'] = true;
// if (story == this.active_story) return;
// // NEWSBLUR.log(['Mousemove feed view', from_top, closest, positions[closest]]);
// NEWSBLUR.app.story_titles.scroll_to_selected_story(story);
var from_top = NEWSBLUR.reader.cache.mouse_position_y + this.$el.scrollTop();
var offset = this.cache.story_pane_position;
var position = from_top - offset;
var positions = this.cache.feed_view_story_positions_keys;
var closest = $.closest(position, positions);
var story = this.cache.feed_view_story_positions[positions[closest]];
// }
if (!story) return;
if (!story.get('selected')) {
story.set('selected', true, {selected_by_scrolling: true, mouse: true, immediate: true});
}
},
handle_scroll_feed_view: function(elem, e) {
var self = this;
var story_view = NEWSBLUR.reader.story_view;
// NEWSBLUR.log(['handle_scroll_feed_view', this.story_view, this.flags['switching_to_feed_view'], this.flags['scrolling_by_selecting_story_title']]);
if ((this.story_view == 'feed' ||
(this.story_view == 'page' && this.flags['page_view_showing_feed_view'])) &&
!this.flags['scrolling_by_selecting_story_title'] &&
// NEWSBLUR.log(['handle_scroll_feed_view', story_view, NEWSBLUR.reader.flags['switching_to_feed_view'], NEWSBLUR.reader.flags['scrolling_by_selecting_story_title']]);
if ((story_view == 'feed' ||
(story_view == 'page' && NEWSBLUR.reader.flags['page_view_showing_feed_view'])) &&
!NEWSBLUR.reader.flags['scrolling_by_selecting_story_title'] &&
!NEWSBLUR.assets.preference('feed_view_single_story')) {
var from_top = this.cache.mouse_position_y + this.$s.$feed_stories.scrollTop();
var from_top = NEWSBLUR.reader.cache.mouse_position_y + this.$el.scrollTop();
var offset = this.cache.story_pane_position;
var position = from_top - offset;
var positions = this.cache.feed_view_story_positions_keys;
var closest = $.closest(position, positions);
var story = this.cache.feed_view_story_positions[positions[closest]];
// NEWSBLUR.log(['Scroll feed view', from_top, e, closest, positions[closest], this.cache.feed_view_story_positions_keys, positions, self.cache]);
NEWSBLUR.app.story_titles.scroll_to_selected_story(story);
if (!story) return;
// console.log(["Scroll Feed", from_top, offset, position, closest, story.get('story_title')]);
if (!story.get('selected')) {
story.set('selected', true, {selected_by_scrolling: true, mouse: true, immediate: true});
}
this.check_feed_view_scrolled_to_bottom();
}
if ((this.flags.river_view || this.flags.social_view) &&
if ((NEWSBLUR.reader.flags['river_view'] || NEWSBLUR.reader.flags['social_view']) &&
!NEWSBLUR.assets.preference('feed_view_single_story')) {
var story;
if (this.flags.scrolling_by_selecting_story_title) {
if (NEWSBLUR.reader.flags['scrolling_by_selecting_story_title']) {
story = this.active_story;
} else {
var from_top = Math.max(1, this.$s.$feed_stories.scrollTop());
var from_top = Math.max(1, this.$el.scrollTop());
var positions = this.cache.feed_view_story_positions_keys;
var closest = $.closest(from_top, positions);
story = this.cache.feed_view_story_positions[positions[closest]];

View file

@ -3,6 +3,9 @@ NEWSBLUR.Views.StoryShareView = Backbone.View.extend({
events: {
"click .NB-feed-story-share" : "toggle_feed_story_share_dialog",
"click .NB-sideoption-share-save" : "mark_story_as_shared",
"click .NB-sideoption-share-unshare" : "mark_story_as_unshared",
"click .NB-sideoption-share-crosspost-twitter" : "toggle_twitter",
"click .NB-sideoption-share-crosspost-facebook" : "toggle_facebook",
"keyup .NB-sideoption-share-comments" : "update_share_button_label"
},
@ -12,7 +15,8 @@ NEWSBLUR.Views.StoryShareView = Backbone.View.extend({
render: function() {
this.$el.html(this.template({
story: this.model
story: this.model,
social_services: NEWSBLUR.assets.social_services
}));
return this;
@ -22,10 +26,18 @@ NEWSBLUR.Views.StoryShareView = Backbone.View.extend({
<div class="NB-sideoption-share-wrapper">\
<div class="NB-sideoption-share">\
<div class="NB-sideoption-share-wordcount"></div>\
<div class="NB-sideoption-share-optional">Optional</div>\
<div class="NB-sideoption-share-crosspost">\
<% if (social_services.twitter.twitter_uid) { %>\
<div class="NB-sideoption-share-crosspost-twitter"></div>\
<% } %>\
<% if (social_services.facebook.facebook_uid) { %>\
<div class="NB-sideoption-share-crosspost-facebook"></div>\
<% } %>\
</div>\
<div class="NB-sideoption-share-title">Comments:</div>\
<textarea class="NB-sideoption-share-comments"><%= story.get("shared_comments") %></textarea>\
<div class="NB-menu-manage-story-share-save NB-modal-submit-green NB-sideoption-share-save NB-modal-submit-button">Share</div>\
<div class="NB-menu-manage-story-share-unshare NB-modal-submit-grey NB-sideoption-share-unshare NB-modal-submit-button">Delete share</div>\
</div>\
</div>\
'),
@ -38,6 +50,9 @@ NEWSBLUR.Views.StoryShareView = Backbone.View.extend({
var $story_content = this.$('.NB-feed-story-content');
var $comment_input = this.$('.NB-sideoption-share-comments');
var $story_comments = this.$('.NB-feed-story-comments');
var $unshare_button = this.$('.NB-sideoption-share-unshare');
var $twitter_button = this.$('.NB-sideoption-share-crosspost-twitter');
var $facebook_button = this.$('.NB-sideoption-share-crosspost-facebook');
if (options.close ||
($sideoption.hasClass('NB-active') && !options.resize_open)) {
@ -59,14 +74,20 @@ NEWSBLUR.Views.StoryShareView = Backbone.View.extend({
}, {
'duration': 300,
'easing': 'easeInOutQuint',
'queue': false
// 'complete': _.bind(this.fetch_story_locations_in_feed_view, this, {'reset_timer': true})
'queue': false,
'complete': function() {
NEWSBLUR.app.story_list.fetch_story_locations_in_feed_view();
}
});
$story_content.removeData('original_height');
}
} else {
// Open/resize
this.$('.NB-error').remove();
$sideoption.addClass('NB-active');
$unshare_button.toggleClass('NB-hidden', !this.model.get("shared"));
$twitter_button.toggleClass('NB-active', !!NEWSBLUR.assets.preference('post_to_twitter'));
$facebook_button.toggleClass('NB-active', !!NEWSBLUR.assets.preference('post_to_facebook'));
var $share_clone = $share.clone();
var full_height = $share_clone.css({
'height': 'auto',
@ -100,8 +121,10 @@ NEWSBLUR.Views.StoryShareView = Backbone.View.extend({
}, {
'duration': 350,
'easing': 'easeInOutQuint',
'queue': false
// 'complete': _.bind(this.fetch_story_locations_in_feed_view, this, {'reset_timer': true})
'queue': false,
'complete': function() {
NEWSBLUR.app.story_list.fetch_story_locations_in_feed_view();
}
}).data('original_height', original_height);
}
this.update_share_button_label();
@ -109,38 +132,72 @@ NEWSBLUR.Views.StoryShareView = Backbone.View.extend({
e.preventDefault();
this.mark_story_as_shared({'source': 'sideoption'});
}, this);
$('.NB-sideoption-share-comments', $share).bind('keydown', 'ctrl+return,meta+return', share);
var $comments = $('.NB-sideoption-share-comments', $share);
$comments.unbind('keydown.story_share')
.bind('keydown.story_share', 'ctrl+return', share)
.bind('keydown.story_share', 'meta+return', share);
}
},
mark_story_as_shared: function(options) {
options = options || {};
var $share_star = this.model.story_title_view.$('.NB-storytitles-share');
var $share_button = this.$('.NB-sideoption-share-save');
var $share_button_menu = $('.NB-menu-manage-story-share-save');
var $share_menu = $share_button_menu.closest('.NB-sideoption-share');
var $share_sideoption = this.model.story_view.$('.NB-feed-story-share .NB-sideoption-title');
var $comments_sideoptions = this.$('.NB-sideoption-share-comments');
var $comments_menu = $('.NB-sideoption-share-comments', $share_menu);
var comments = _.string.trim((options.source == 'menu' ? $comments_menu : $comments_sideoptions).val());
var feed = NEWSBLUR.reader.model.get_feed(NEWSBLUR.reader.active_feed);
var feed = NEWSBLUR.assets.get_feed(NEWSBLUR.reader.active_feed);
var source_user_id = feed && feed.get('user_id');
var post_to_services = _.compact([
NEWSBLUR.assets.preference('post_to_twitter') && 'twitter',
NEWSBLUR.assets.preference('post_to_facebook') && 'facebook'
]);
this.model.set("shared", true);
$share_button.addClass('NB-saving').addClass('NB-disabled').text('Sharing...');
$share_button_menu.addClass('NB-saving').addClass('NB-disabled').text('Sharing...');
NEWSBLUR.assets.mark_story_as_shared(this.model.id, this.model.get('story_feed_id'), comments, source_user_id, _.bind(function() {
this.toggle_feed_story_share_dialog({'close': true});
NEWSBLUR.reader.hide_confirm_story_share_menu_item(true);
$share_button.removeClass('NB-saving').removeClass('NB-disabled').text('Share');
$share_sideoption.text('Shared').closest('.NB-sideoption');
this.model.story_view.$el.addClass('NB-story-shared');
$comments_menu.val(comments);
$comments_sideoptions.val(comments);
this.model.story_view.render_comments();
$share_star.attr({'title': 'Shared!'});
NEWSBLUR.assets.mark_story_as_shared(this.model.id, this.model.get('story_feed_id'), comments, source_user_id, post_to_services, _.bind(this.post_share_story, this, true), _.bind(function(data) {
this.post_share_error(data, true);
}, this));
NEWSBLUR.reader.blur_to_page();
},
mark_story_as_unshared: function(options) {
options = options || {};
var $unshare_button = this.$('.NB-sideoption-share-unshare');
var $unshare_button_menu = $('.NB-menu-manage-story-share-unshare');
var $share_menu = $unshare_button_menu.closest('.NB-sideoption-share');
$unshare_button.addClass('NB-saving').addClass('NB-disabled').text('Deleting...');
NEWSBLUR.assets.mark_story_as_unshared(this.model.id, this.model.get('story_feed_id'), _.bind(this.post_share_story, this, false), _.bind(function(data) {
this.post_share_error(data, false);
}, this));
NEWSBLUR.reader.blur_to_page();
},
post_share_story: function(shared) {
this.model.set("shared", shared);
var $share_star = this.model.story_title_view.$('.NB-storytitles-share');
var $share_button = this.$('.NB-sideoption-share-save');
var $unshare_button = this.$('.NB-sideoption-share-unshare');
var $share_sideoption = this.model.story_view.$('.NB-feed-story-share .NB-sideoption-title');
var $comments_sideoptions = this.$('.NB-sideoption-share-comments');
var shared_text = this.model.get('shared') ? 'Shared' : 'Unshared';
this.toggle_feed_story_share_dialog({'close': true});
NEWSBLUR.reader.hide_confirm_story_share_menu_item(true);
$share_button.removeClass('NB-saving').removeClass('NB-disabled').text('Share');
$unshare_button.removeClass('NB-saving').removeClass('NB-disabled').text('Delete Share');
$share_sideoption.text(shared_text).closest('.NB-sideoption');
this.model.story_view.$el.toggleClass('NB-story-shared', this.model.get('shared'));
this.model.story_view.render_comments();
if (this.model.get('shared')) {
$share_star.attr({'title': shared_text + '!'});
$share_star.tipsy({
gravity: 'sw',
fade: true,
@ -157,25 +214,34 @@ NEWSBLUR.Views.StoryShareView = Backbone.View.extend({
tipsy.disable();
}
}, 850);
// this.fetch_story_locations_in_feed_view({'reset_timer': true});
}, this), _.bind(function(data) {
var message = data && data.message || "Sorry, this story could not be shared. Probably a bug.";
if (!NEWSBLUR.Globals.is_authenticated) {
message = "You need to be logged in to share a story.";
}
var $error = $.make('div', { className: 'NB-error' }, message);
$share_button.removeClass('NB-saving').removeClass('NB-disabled').text('Share');
$share_button.siblings('.NB-error').remove();
$share_button.after($error);
if ($share_button_menu.length) {
$share_button_menu.removeClass('NB-disabled').text('Share');
$share_button_menu.siblings('.NB-error').remove();
$share_button_menu.after($error.clone());
}
this.toggle_feed_story_share_dialog({'resize_open': true});
}, this));
}
NEWSBLUR.reader.blur_to_page();
NEWSBLUR.app.story_list.fetch_story_locations_in_feed_view();
},
post_share_error: function(data, shared) {
var $share_button = this.$('.NB-sideoption-share-save');
var $unshare_button = this.$('.NB-sideoption-share-unshare');
var $share_button_menu = $('.NB-menu-manage-story-share-save');
var message = data && data.message || ("Sorry, this story could not be " + (shared ? "" : "un") + "shared. Probably a bug.");
console.log(["post_share_error", data, shared, message]);
if (!NEWSBLUR.Globals.is_authenticated) {
message = "You need to be logged in to share a story.";
}
var $error = $.make('div', { className: 'NB-error' }, message);
$share_button.removeClass('NB-saving').removeClass('NB-disabled').text('Share');
$unshare_button.removeClass('NB-saving').removeClass('NB-disabled').text('Delete Share');
$share_button.siblings('.NB-error').remove();
$share_button.after($error);
if ($share_button_menu.length) {
$share_button_menu.removeClass('NB-disabled').text('Share');
$share_button_menu.siblings('.NB-error').remove();
$share_button_menu.after($error.clone());
}
this.toggle_feed_story_share_dialog({'resize_open': true});
},
update_share_button_label: function() {
@ -195,6 +261,30 @@ NEWSBLUR.Views.StoryShareView = Backbone.View.extend({
count_selected_words_when_sharing_story: function($feed_story) {
var $wordcount = $('.NB-sideoption-share-wordcount', $feed_story);
},
toggle_twitter: function() {
var $twitter_button = this.$('.NB-sideoption-share-crosspost-twitter');
if (NEWSBLUR.assets.preference('post_to_twitter')) {
NEWSBLUR.assets.preference('post_to_twitter', false);
} else {
NEWSBLUR.assets.preference('post_to_twitter', true);
}
$twitter_button.toggleClass('NB-active', NEWSBLUR.assets.preference('post_to_twitter'));
},
toggle_facebook: function() {
var $facebook_button = this.$('.NB-sideoption-share-crosspost-facebook');
if (NEWSBLUR.assets.preference('post_to_facebook')) {
NEWSBLUR.assets.preference('post_to_facebook', false);
} else {
NEWSBLUR.assets.preference('post_to_facebook', true);
}
$facebook_button.toggleClass('NB-active', NEWSBLUR.assets.preference('post_to_facebook'));
}
});

View file

@ -4,7 +4,7 @@ NEWSBLUR.Views.StoryTitlesView = Backbone.View.extend({
events: {
"click .NB-feed-story-premium-only a" : function() {
NEWSBLUR.reader.open_feedchooser_dialog();
NEWSBLUR.reader.open_feedchooser_modal();
}
},
@ -12,6 +12,7 @@ NEWSBLUR.Views.StoryTitlesView = Backbone.View.extend({
_.bindAll(this, 'scroll');
this.collection.bind('reset', this.render, this);
this.collection.bind('add', this.add, this);
this.collection.bind('no_more_stories', this.check_premium_river, this);
NEWSBLUR.reader.$s.$story_titles.scroll(this.scroll);
},
@ -38,6 +39,7 @@ NEWSBLUR.Views.StoryTitlesView = Backbone.View.extend({
var collection = this.collection;
if (options.added) {
var $stories = _.map(this.collection.models.slice(-1 * options.added), function(story) {
if (story.story_title_view) return;
return new NEWSBLUR.Views.StoryTitleView({
model: story,
collection: collection
@ -62,6 +64,7 @@ NEWSBLUR.Views.StoryTitlesView = Backbone.View.extend({
this.$('.NB-feed-story-premium-only').remove();
this.$el.append($notice);
},
// ===========
// = Actions =
// ===========
@ -148,6 +151,11 @@ NEWSBLUR.Views.StoryTitlesView = Backbone.View.extend({
}
},
check_premium_river: function() {
this.show_no_more_stories();
this.append_river_premium_only_notification();
},
end_loading: function() {
var $endbar = NEWSBLUR.reader.$s.$story_titles.find('.NB-story-titles-end-stories-line');
$endbar.remove();
@ -155,10 +163,6 @@ NEWSBLUR.Views.StoryTitlesView = Backbone.View.extend({
if (NEWSBLUR.assets.flags['no_more_stories']) {
this.show_no_more_stories();
} else if (NEWSBLUR.reader.flags['non_premium_river_view'] &&
this.collection.visible().length >= NEWSBLUR.reader.constants.RIVER_STORIES_FOR_STANDARD_ACCOUNT) {
this.show_no_more_stories();
this.append_river_premium_only_notification();
}
},

View file

@ -10,7 +10,7 @@ from utils import jammit
# ===================
ADMINS = (
('Samuel Clay', 'samuel@ofbrooklyn.com'),
('Samuel Clay', 'samuel@newsblur.com'),
)
SERVER_EMAIL = 'server@newsblur.com'
@ -94,6 +94,7 @@ MIDDLEWARE_CLASSES = (
'django.contrib.auth.middleware.AuthenticationMiddleware',
'apps.profile.middleware.LastSeenMiddleware',
'apps.profile.middleware.SQLLogToConsoleMiddleware',
'subdomains.middleware.SubdomainMiddleware',
# 'debug_toolbar.middleware.DebugToolbarMiddleware',
)
@ -180,6 +181,16 @@ TEST_RUNNER = "utils.testrunner.TestRunner"
SESSION_COOKIE_NAME = 'newsblur_sessionid'
SESSION_COOKIE_AGE = 60*60*24*365*2 # 2 years
# ==============
# = Subdomains =
# ==============
SUBDOMAIN_URLCONFS = {
None: 'urls',
'www': 'urls',
}
REMOVE_WWW_FROM_DOMAIN = True
# ===========
# = Logging =
# ===========
@ -240,6 +251,10 @@ ZEBRA_ENABLE_APP = True
import djcelery
djcelery.setup_loader()
CELERY_ROUTES = {
"work-queue": {
"queue": "work_queue",
"binding_key": "work_queue"
},
"new-feeds": {
"queue": "new_feeds",
"binding_key": "new_feeds"
@ -254,6 +269,11 @@ CELERY_ROUTES = {
},
}
CELERY_QUEUES = {
"work_queue": {
"exchange": "work_queue",
"exchange_type": "direct",
"binding_key": "work_queue",
},
"new_feeds": {
"exchange": "new_feeds",
"exchange_type": "direct",
@ -270,13 +290,13 @@ CELERY_QUEUES = {
"binding_key": "update_feeds"
},
}
CELERY_DEFAULT_QUEUE = "update_feeds"
BROKER_BACKEND = "redis"
CELERY_DEFAULT_QUEUE = "work_queue"
BROKER_BACKEND = "redis"
BROKER_URL = "redis://db01:6379/0"
CELERY_REDIS_HOST = "db01"
CELERY_REDIS_HOST = "db01"
CELERYD_PREFETCH_MULTIPLIER = 1
CELERY_IMPORTS = ("apps.rss_feeds.tasks", )
CELERY_IMPORTS = ("apps.rss_feeds.tasks", "apps.social.tasks", )
CELERYD_CONCURRENCY = 4
CELERY_IGNORE_RESULT = True
CELERY_ACKS_LATE = True # Retry if task fails

View file

@ -1,16 +1,17 @@
{% block body %}{% endblock body %}
- Samuel Clay, @samuelclay
- Samuel & Roy
-----------------------------------------------------------------------------
Stay up to date and in touch with me, yr. developer, in a few different ways:
NewsBlur is your social news reader. A new sound of an old instrument.
Stay up to date and in touch with us, yr. developers, in a few different ways:
* Follow @samuelclay on Twitter: http://twitter.com/samuelclay/
* Follow @newsblur on Twitter: http://twitter.com/newsblur/
* Follow @samuelclay on GitHub: http://github.com/samuelclay/
{% block resources_header %}There are a few resources you can use if you end up loving NewsBlur:{% endblock resources_header %}
{% block resources_header %}To get the most out of NewsBlur, here are a few resources:{% endblock resources_header %}
* Read the NewsBlur Blog: http://blog.newsblur.com
* Get support on NewsBlur's Get Satisfaction: http://getsatisfaction.com/newsblur/

View file

@ -20,7 +20,7 @@
{% block body %}{% endblock %}
<p style="line-height:20px;">- Samuel Clay, <a href="http://twitter.com/samuelclay" style="text-decoration:none">@samuelclay</a></p>
<p style="line-height:20px;clear: both;">- Samuel &amp; Roy</p>
</td>
</tr>
<tr>
@ -28,22 +28,22 @@
<table>
<tr>
<td>
<p style="line-height:20px;">Stay up to date and in touch with me, yr. developer, in a few different ways:</p>
<p style="line-height:20px;">NewsBlur is your social news reader. A new sound of an old instrument.</p>
<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/samuelclay/" style="text-decoration:none"><img src="http://www.newsblur.com/media/img/reader/twitter_icon.png" style="width:16px;height:16px;vertical-align:top;padding-top:3px;"> Follow @samuelclay on Twitter</a>.</li>
<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>. &larr; I live on props.</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>
</ul>
</p>
<p style="line-height: 20px;">{% block resources_header %}There are a couple resources you can use if you end up loving NewsBlur:{% endblock resources_header %}</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>
</ul>
</p>
<p style="line-height: 20px;">There's plenty of ways to use NewsBlur beyond the website:</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>

View file

@ -6,3 +6,5 @@
<p style="line-height: 20px;">Spend a few days trying out NewsBlur. I hope you end up loving it.</p>
<p style="line-height: 20px;">If you really do love using NewsBlur you should purchase a fancy <b>premium account</b> for only $12/year (nights and weekends and all major public holidays included). You get to support an independent developer, help feed his dog, and contribute to the long-term health of NewsBlur.</p>
{% endblock %}
{% block resources_header %}There are a couple resources you can use if you end up loving NewsBlur:{% endblock resources_header %}

View file

@ -0,0 +1,14 @@
{% extends "mail/email_base.txt" %}
{% block body %}Say hello to your newest follower: {{ follower_profile.username }}.
{% if common_followers %}
You follow {{ common_followers|length }} {{ common_followers|pluralize:"person,people" }} who follow{{ common_followers|pluralize:"s," }} {{ follower_profile.username }}:
{% for follower in common_followers %} * {{ follower.username }}: {{ follower.blurblog_url }}
{% endfor %}{% endif %}
{% if common_followings %}You follow {{ common_followings|length }} {{ common_followings|pluralize:"person,people" }} that {{ follower_profile.username }} also follows:
{% for following in common_followings %} * {{ following.username }}: {{ following.blurblog_url }}
{% endfor %}{% endif %}{% endblock %}

View file

@ -0,0 +1,36 @@
{% extends "mail/email_base.xhtml" %}
{% block body %}
<p style="font-size: 37px; color:#555555; margin-top: 18px;margin-bottom: 10px;padding-top:6px;">
Say hello to your newest follower:
</p>
<div style="margin: 24px 0;font-size: 24px;text-align: center;">
<a href="{{ follower_profile.blurblog_url }}"><img src="{{ follower_profile.photo_url }}" style="max-width: 200px; max-height: 200px;margin: 0 auto;border-radius: 3px;border: none;"></a>
<div style="margin: 8px 0 0 0;font-weight: bold;"><a href="{{ follower_profile.blurblog_url }}" style="color: #404040;text-decoration: none">{{ follower_profile.username }}</a></div>
</div>
{% if common_followers %}
<div style="clear: both; overflow: hidden">
<p style="margin: 12px 0 12px;line-height: 16px;clear: both;">You follow {{ common_followers|length }} {{ common_followers|pluralize:"person,people" }} who follow{{ common_followers|pluralize:"s," }} {{ follower_profile.username }}:</p>
{% for follower in common_followers %}
<div style="margin: 0 12px 12px 0; float: left;">
<a href="{{ follower.blurblog_url }}"><img src="{{ follower.email_photo_url }}" style="float: left; border-radius: 3px;margin-right: 6px; width: 24px; height: 24px;border: none;"></a>
<a href="{{ follower.blurblog_url }}" style="color: #404040;text-decoration: none">{{ follower.username }}</a>
</div>
{% endfor %}
</div>
{% endif %}
{% if common_followings %}
<div style="clear: both; overflow: hidden">
<p style="display: block; margin: 12px 0 12px;line-height: 16px;clear: both;">You and {{ follower_profile.username }} both follow {{ common_followings|length }} {{ common_followings|pluralize:"person,people" }}:</p>
{% for following in common_followings %}
<div style="margin: 0 12px 12px 0; float: left;">
<a href="{{ following.blurblog_url }}"><img src="{{ following.email_photo_url }}" style="float: left; border-radius: 3px;margin-right: 6px; width: 24px; height: 24px;border: none;"></a>
<a href="{{ following.blurblog_url }}" style="color: #404040;text-decoration: none">{{ following.username }}</a>
</div>
{% endfor %}
</div>
{% endif %}
{% endblock %}

View file

@ -80,7 +80,7 @@
<div class="NB-signup-optional">
{% if login_form.errors %}
{% for field, error in login_form.errors.items %}
<a href="mailto:samuel@ofbrooklyn.com?subject=Forgot%20Password%20on%20NewsBlur&amp;body=Hello!%20My%20username%20is:%20" class="NB-splash-link">Forgot password?</a>
<a href="mailto:password@newsblur.com?subject=Forgot%20Password%20on%20NewsBlur&amp;body=Hello!%20My%20username%20is:%20" class="NB-splash-link">Forgot password?</a>
{% endfor %}
{% else %}
Optional

View file

@ -1,11 +1,15 @@
{% load utils_tags %}
<!DOCTYPE html>
<html>
<head>
<title>{{ social_profile.feed_title }}</title>
<link rel="alternate" type="application/rss+xml" href="{% url shared-stories-rss-feed social_profile.user_id social_profile.username|slugify %}" title="{{ social_profile.feed_title }} RSS feed">
<link rel="shortcut icon" HREF="{{ social_profile.photo_url }}">
<link rel="stylesheet" href="{{ MEDIA_URL }}css/social/social_page.css" type="text/css" media="screen" title="no title" charset="utf-8">
<link rel="shortcut icon" HREF="{{ social_profile.photo_url }}">
{% include_stylesheets "blurblog" %}
{% if social_profile.custom_css %}
<style type="text/css" media="screen">
{{ social_profile.custom_css|safe }}
@ -15,9 +19,10 @@
<body>
<div class="NB-page">
<header class="NB-header">
<h1 class="NB-title {% if not social_profile.bio %}NB-title-no-bio{% endif %}">
<a href="{% url index %}social/{{ social_profile.user_id }}/{{ social_profile.username|slugify }}" class="NB-title-logo">
<a href="http://{% current_domain %}/social/{{ social_profile.user_id }}/{{ social_profile.username|slugify }}" class="NB-title-logo">
<img src="{{ MEDIA_URL }}img/logo_128.png" class="NB-hover-off" />
<img src="{{ MEDIA_URL }}img/logo_newsblur_512.png" class="NB-hover-on" />
</a>
@ -58,8 +63,7 @@
<div class="NB-feed NB-feed-{{ story.feed.favicon_text_color }}" style="
background-image: -webkit-gradient( linear, left bottom, left top, color-stop(0.16, #{{ story.feed.favicon_color }}), color-stop(0.84, #{{ story.feed.favicon_fade }}) );
background-image: -moz-linear-gradient( center bottom, #{{ story.feed.favicon_color }} 16%, #{{ story.feed.favicon_fade }} 84% );
border-top-color: #{{ story.feed.favicon_border }};
border-bottom-color: #{{ story.feed.favicon_border }};
border: 1px solid #{{ story.feed.favicon_border }};
">
{% if story.feed %}
<div class="NB-feed-favicon">
@ -71,31 +75,116 @@
{% endif %}
</div>
<div class="NB-story-header">
{% if story.story_authors %}
<div class="NB-story-author">{{ story.story_authors }}</div>
{% endif %}
{% if story.story_tags %}
<div class="NB-story-tags">
{% for story_tag in story.story_tags %}
<div class="NB-story-tag">{{ story_tag }}</div>
{% endfor %}
<div class="NB-story-header-wrapper">
<div class="NB-story-header">
{% if story.story_authors %}
<div class="NB-story-author">{{ story.story_authors }}</div>
{% endif %}
{% if story.story_tags %}
<div class="NB-story-tags">
{% for story_tag in story.story_tags %}
<div class="NB-story-tag">{{ story_tag }}</div>
{% endfor %}
</div>
{% endif %}
<div class="NB-story-title">
<a href="{{ story.story_permalink }}">{{ story.story_title }}</a>
</div>
<div class="NB-story-date">
{% if story.has_modifications %}
<div class="NB-story-modifications-button" title="Show story changes"></div>
{% endif %}
{{ story.shared_date|date:"l, F j<\s\u\p>S</\s\u\p>, Y <\s\m\a\l\l>\a\t</\s\m\a\l\l> g:i A"|safe }}
</div>
{% endif %}
<div class="NB-story-title">
<a href="{{ story.story_permalink }}">{{ story.story_title }}</a>
</div>
<div class="NB-story-date">
{{ story.shared_date|date:"g:i A <\s\m\a\l\l>\o\n</\s\m\a\l\l> l, F j<\s\u\p>S</\s\u\p>, Y"|safe }}
</div>
</div>
<div class="NB-story">
<div class="NB-story-content">{{ story.story_content|safe }}</div>
{% if story.share_count %}
<div class="NB-story-comments">
<div class="NB-story-comments-shares-teaser-wrapper">
<div class="NB-story-comments-shares-teaser">
{% if story.share_count %}
<div class="NB-right">
Shared by
<b>{{ story.share_count }}</b>
{{ story.share_count|pluralize:"person, people" }}
</div>
{% if story.share_count_public %}
<div class="NB-story-share-profiles NB-story-share-profiles-public">
{% for share_user in story.shared_by_public %}
<a href="{{ share_user.feed_link }}" class="NB-user-avatar" title="{{ share_user.username }}">
<img src="{{ share_user.photo_url }}">
</a>
{% endfor %}
</div>
{% endif %}
{% endif %}
{% if story.share_count %}
{% if story.share_count_friends %}
<div class="NB-story-share-label">Shared by: </div>
<div class="NB-story-share-profiles NB-story-share-profiles-friends">
{% for share_user in story.shared_by_friends %}
<a href="{{ share_user.feed_link }}" class="NB-user-avatar" title="{{ share_user.username }}">
<img src="{{ share_user.photo_url }}">
</a>
{% endfor %}
</div>
{% endif %}
{% endif %}
</div>
</div>
{% for comment in story.comments %}
<div class="NB-story-comment">
<a href="{{ comment.user.feed_link }}" class="NB-user-avatar {% if comment.source_user_id %}NB-story-comment-reshare{% endif %}">
<img src="{{ comment.user.photo_url }}">
</a>
<div class="NB-story-comment-author-container">
{% if comment.source_user_id %}
<div class="NB-story-comment-reshares">
<a href="{{ comment.source_user.feed_link }}" class="NB-user-avatar" title="{{ comment.source_user.username }}">
<img src="{{ comment.source_user.photo_url }}">
</a>
</div>
{% endif %}
<a href="{{ comment.user.feed_link }}" class="NB-story-comment-username">{{ comment.user.username }}</a>
<div class="NB-story-comment-date">{{ comment.shared_date }} ago</div>
<div class="NB-story-comment-reply-button">
<div class="NB-story-comment-reply-button-wrapper">reply</div>
</div>
</div>
<div class="NB-story-comment-content">
{{ comment.comments }}
</div>
{% if comment.replies %}
<div class="NB-story-comment-replies">
{% for reply in comment.replies %}
<div class="NB-story-comment-reply">
<a href="{{ reply.user.feed_link }}">
<img class="NB-user-avatar NB-story-comment-reply-photo" src="{{ reply.user.photo_url }}" />
</a>
<a href="{{ reply.user.feed_link }}" class="NB-story-comment-username NB-story-comment-reply-username">{{ reply.user.username }}</a>
<div class="NB-story-comment-date NB-story-comment-reply-date">{{ reply.publish_date }} ago</div>
<div class="NB-story-comment-reply-content">{{ reply.comments }}</div>
</div>
{% endfor %}
</div>
{% endif %}
</div>
{% endfor %}
</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
{% include_javascripts "blurblog" %}
</body>
</html>

View file

@ -20,7 +20,7 @@
<ul class="NB-about-who">
<li>Hand-crafted by: <a href="http://www.samuelclay.com">Samuel Clay</a></li>
<li>Talk to him on Twitter: <a href="http://twitter.com/samuelclay">@samuelclay</a></li>
<li>E-mail: <a href="mailto:samuel@ofbrooklyn.com">samuel@ofbrooklyn.com</a></li>
<li>E-mail: <a href="mailto:samuel@newsblur.com">samuel@newsblur.com</a></li>
<li class="last">Created in: <a href="http://www.newyorkfieldguide.com">New York City</a> (mostly on the A train)</li>
</ul>
</div>

View file

@ -14,17 +14,22 @@
<div class="NB-module">
<h5 class="NB-module-title">Introduction to the API</h5>
<div class="NB-module-content">
<p><a href="/">NewsBlur</a> is an RSS feed reader with intelligence.</p>
<p><a href="/">NewsBlur</a> is a social news reader with intelligence. A new sound of
an old instrument.</p>
<p>NewsBlur's API allows users to retrieve their feeds, feed counts, feed icons, feed
statistics, and individual feed stories. No API key is required, but you are required
to authenticate before using any of the API endpoints. Please be considerate, and don't
hammer our servers.</p>
<p>It is a very nice thing to note that this entire API is open-source, including
<p>We're quite pleased to point out that this entire API is open-source, including
the implementation of the endpoints. You can find the source of the
<a href="http://github.com/samuelclay/NewsBlur/tree/master/apps/reader/views.py">/reader/ views</a>,
<a href="http://github.com/samuelclay/NewsBlur/tree/master/apps/rss_feeds/views.py">/rss_feeds/ views</a>,
<a href="http://github.com/samuelclay/NewsBlur/tree/master/apps/reader/views.py">/reader/ views source</a>,
<a href="http://github.com/samuelclay/NewsBlur/tree/master/apps/social/views.py">/social/ views source</a>,
<a href="http://github.com/samuelclay/NewsBlur/tree/master/apps/rss_feeds/views.py">/rss_feeds/ views source</a>,
as well as
<a href="http://github.com/samuelclay/NewsBlur/tree/master/templates/static/api.yml">the API definitions in YAML</a>.</p>
<p>We love pull requests. If you want to add an endpoint, modify output, or make
something better, <a href="http://github.com/samuelclay/NewsBlur">NewsBlur's repo
on Github</a> is the place to make that happen.</p>
</div>
</div>

View file

@ -39,7 +39,7 @@
- key: email
desc: "Email address"
optional: true
example: "samuel@ofbrooklyn.com"
example: "samuel@newsblur.com"
- feeds:
- url: /rss_feeds/search_feed
@ -108,7 +108,7 @@
To use inline data images, you can use this syntax:
<pre><code>&lt;img src="data:image/png;base64,[IMAGE_DATA_STRING]" /&gt;</code></pre>
params:
- key: feeds
- key: feed_ids
desc: " Array of feed ids. Leave empty to retrieve all active (enabled) feeds."
optional: true
example: "[1, 2, 3]"
@ -231,7 +231,9 @@
- key: story_id
desc: "List of story ids to mark as read."
required: true
example: "[12, 24, 36]"
example: >
["http://blog.newsblur.com/post/1",
"http://blog.newsblur.com/post/2"]
- key: feed_id
desc: "Feed id that each story is from."
required: true
@ -309,7 +311,239 @@
optional: true
default: "0"
example: "7"
- social:
- url: /social/share_story
method: POST
short_desc: "Share a story with optional comments."
long_desc:
- "Share a story with optional comments. The story is shared on the user's blurblog."
params:
- key: feed_id
desc: "Feed id that the story is from."
required: true
example: "42"
- key: story_id
desc: "Story id to share."
required: true
example: "http://blog.newsblur.com/post/1"
- key: comments
desc: "The meat of the share."
optional: true
example: "42"
- key: source_user_id
desc: >
If the story is being re-shared from another blurblog, note the
user_id of the original sharer.
optional: true
example: "128"
- key: post_to_services
desc: "List of services to cross-post to. Can be 'twitter' and/or 'facebook'."
optional: true
example: "['twitter', 'facebook']"
- url: /social/unshare_story
method: POST
short_desc: "Remove a shared story from user's blurblog."
long_desc:
- >
Removes a shared story from a user's blurblog. Unshares the story and
removes the comments. Any replies will also be deleted. This may change.
params:
- key: feed_id
desc: "Feed id that the story is from."
required: true
example: "42"
- key: story_id
desc: "Story id to remove the existing share."
required: true
example: "http://blog.newsblur.com/post/1"
- url: /social/load_user_friends
method: GET
short_desc: "Lists of user profiles: followers, followings, etc."
long_desc:
- This lists the user profiles of followers and followings of a user.
- Also returns information related to which sharing services a user is using.
- url: /social/profile
method: GET
short_desc: "Public profile of a user."
long_desc:
- "Public profile of a user. Includes common followers and common followings."
params:
- key: user_id
desc: "User id of user"
required: true
example: "42"
- url: /social/load_user_profile
method: GET
short_desc: "Private details of the current user's profile and connected services."
long_desc:
- "Private details of the current user's profile and connected services."
- url: /social/save_user_profile
method: POST
short_desc: "Saves a user's profile details."
long_desc:
- "Saves a user's profile details."
params:
- key: location
desc: "Hometown"
optional: true
example: "New York City"
- key: bio
desc: "160 character bio."
optional: true
example: "Developer. API Designer."
- key: website
desc: "Website address"
optional: true
example: "http://www.samuelclay.com"
- key: photo_service
desc: "Which photo service to use: twitter, facebook, gravatar."
optional: true
example: "twitter"
- url: /social/interactions
method: GET
short_desc: "Interactions between a user and other users."
long_desc:
- "Interactions between a user and other users."
- Currently only serves HTML. Email samuel@newsblur.com if you want this data in JSON.
- url: /social/follow
method: POST
short_desc: "Follow a user and subscribe to their blurblog."
long_desc:
- "Follow a user and subscribe to their blurblog."
params:
- key: user_id
desc: "ID of user to follow."
required: true
example: "42"
- url: /social/unfollow
method: POST
short_desc: "Unfollow a user and unsubscribe from their blurblog."
long_desc:
- "Unfollow a user and unsubscribe from their blurblog."
params:
- key: user_id
desc: "ID of user to unfollow."
required: true
example: "42"
- url: /social/feed_trainer
method: GET
short_desc: "Get the intelligence classifiers for a blurblog."
long_desc:
- "Get the intelligence classifiers for a blurblog."
params:
- key: user_id
desc: "ID of blurblog's user."
required: true
example: "42"
- url: /social/comments
method: GET
short_desc: "Get all public comments on a shared story."
long_desc:
- "Get all public comments on a shared story."
- >
If you want comments from friends, they are already attached to the
story from either <code>reader/feed/:id</code> or
<code>/social/stories/:user_id</code>.
params:
- key: story_id
desc: "ID of shared story."
required: true
example: "http://blog.newsblur.com/post/1"
- key: feed_id
desc: "ID of feed for the shared story."
required: true
example: "42"
- url: /social/save_comment_reply
method: POST
short_desc: "Saves a reply to a comment."
long_desc:
- "Saves a reply to a comment."
- "Comments can have a number of replies, but they are all one-level deep."
- >
Replying to a comment sends notifications to both original commenter and to
every other replier to the comment.
params:
- key: story_feed_id
desc: "ID of feed the story belongs to."
required: true
example: "42"
- key: story_id
desc: "ID of story being commented on."
required: true
example: "http://blog.newsblur.com/post/1"
- key: comment_user_id
desc: "ID of user who left the original comment that is now being replied to."
required: true
example: "64"
- key: reply_comments
desc: "The content of the reply."
required: true
example: "Brilliant analysis."
- key: original_message
desc: >
If editing an existing reply, provide the original reply so the correct
reply can be overwritten.
optional: true
example: "Brilliant analysis, bro."
- url: /social/find_friends
method: GET
short_desc: "Search for a user by username, email, or blurblog title."
long_desc:
- "Search for a user by username, email, or blurblog title."
params:
- key: query
desc: "Username, email, or part of a blurblog title."
required: true
example: "samuel"
- url: /social/rss/:user_id/:username
method: GET
short_desc: "RSS feed for a blurblog."
long_desc:
- "RSS feed for a blurblog."
tips:
- >
The <code>username</code> url parameter is optional. It's just recommended
if you have it.
- url: /social/stories/:user_id/:username
method: GET
short_desc: "Shared stories from a user's blurblog."
long_desc:
- "Shared stories from a user's blurblog."
- "Comments, replies, and profiles from followed users are automatically included."
tips:
- >
The <code>username</code> url parameter is optional. It's just recommended
if you have it.
- url: /social/page/:user_id/:username
method: GET
short_desc: "A user's shared stories blurblog in HTML."
long_desc:
- "A user's shared stories blurblog in HTML."
- "This may also live at a custom URL, which is specified in the user's full profile."
tips:
- >
The <code>username</code> url parameter is optional. It's just recommended
if you have it.
- url: /social/settings/:user_id/:username
method: GET
short_desc: "A user's full profile."
long_desc:
- "A user's full profile."
tips:
- >
The <code>username</code> url parameter is optional. It's just recommended
if you have it.
- url: /social/statistics/:user_id/:name
method: GET
short_desc: "Get statistics and history for a blurblog."
long_desc:
- "Get statistics and history for a blurblog."
tips:
- >
The <code>username</code> url parameter is optional. It's just recommended
if you have it.
- "feed management":
- url: /reader/add_url
method: POST

View file

@ -120,7 +120,7 @@
How I can prevent NewsBlur from loading the Original view (without using an iFrame buster)?
</div>
<div class="NB-faq-answer last">
<p>If you would like to opt-out of the Original view, simply email <a href="mailto:samuel@ofbrooklyn.com">Samuel Clay</a> and let him know. He will place your site on an opt-out list.</p>
<p>If you would like to opt-out of the Original view, simply email <a href="mailto:samuel@newsblur.com">Samuel Clay</a> and let him know. He will place your site on an opt-out list.</p>
<p>Note that users will still be able to read the Feed view. However, consider that when in the Original view, your ads and original content are unaffected. Consider instead placing an exception rule in your iFrame buster code that looks for 'www.newsblur.com' in the URL and prevents your iFrame buster from running.</p>
</div>
</li>
@ -174,7 +174,7 @@
Help! I have an issue and it's not mentioned here.
</div>
<div class="NB-faq-answer last">
Please, please, please e-mail <a href="mailto:samuel@ofbrooklyn.com">samuel@ofbrooklyn.com</a>. If you have an issue it is entirely possible that other people do, too.
Please, please, please e-mail <a href="mailto:samuel@newsblur.com">samuel@newsblur.com</a>. If you have an issue it is entirely possible that other people do, too.
</div>
</li>
</ul>

View file

@ -19,7 +19,7 @@
Thank you for your interest in NewsBlur. NewsBlur is built by a solo developer:
<ul>
<li><a href="http://www.samuelclay.com">Samuel Clay</a>, founder, <a href="mailto:samuel@ofbrooklyn.com">samuel@ofbrooklyn.com</a>, <a href="http://twitter.com/samuelclay">@samuelclay</a></li>
<li><a href="http://www.samuelclay.com">Samuel Clay</a>, founder, <a href="mailto:samuel@newsblur.com">samuel@newsblur.com</a>, <a href="http://twitter.com/samuelclay">@samuelclay</a></li>
</ul>
Contact Samuel for interviews, comments, and questions about NewsBlur. As another
ambitious New Yorker, he is happy to talk about NewsBlur.

31
utils/bootstrap_intel.py Normal file
View file

@ -0,0 +1,31 @@
import sys
from mongoengine.queryset import OperationError
from mongoengine.base import ValidationError
from apps.analyzer.models import MClassifierFeed
from apps.analyzer.models import MClassifierAuthor
from apps.analyzer.models import MClassifierTag
from apps.analyzer.models import MClassifierTitle
for classifier_cls in [MClassifierFeed, MClassifierAuthor,
MClassifierTag, MClassifierTitle]:
print " ================================================================= "
print " Now on %s " % classifier_cls.__name__
print " ================================================================= "
classifiers = classifier_cls.objects.filter(social_user_id__exists=False)
count = classifiers.count()
print " ---> Found %s classifiers" % count
for i, classifier in enumerate(classifiers):
if i % 1000 == 0:
print " ---> %s / %s" % (i, count)
sys.stdout.flush()
classifier.social_user_id = 0
try:
classifier.save()
except OperationError, e:
print " ***> Operation error on: %s" % e
sys.stdout.flush()
# classifier.delete()
except ValidationError, e:
print " ***> ValidationError error on: %s" % e
print " ***> Original classifier: %s" % classifier.__dict__

View file

@ -11,6 +11,7 @@ graph_config = {
'celery_update_feeds.label': 'Celery - Update Feeds',
'celery_new_feeds.label': 'Celery - New Feeds',
'celery_push_feeds.label': 'Celery - Push Feeds',
'celery_work_queue.label': 'Celery - Work Queue',
}
@ -28,6 +29,7 @@ def calculate_metrics():
'celery_update_feeds': r.llen("update_feeds"),
'celery_new_feeds': r.llen("new_feeds"),
'celery_push_feeds': r.llen("push_feeds"),
'celery_work_queue': r.llen("work_queue"),
}
if __name__ == '__main__':

View file

@ -146,7 +146,6 @@ class bunch(dict):
else:
self.__setitem__(item, value)
class MLStripper(HTMLParser):
def __init__(self):
self.reset()
@ -159,4 +158,16 @@ class MLStripper(HTMLParser):
def strip_tags(html):
s = MLStripper()
s.feed(html)
return s.get_data()
return s.get_data()
def truncate_chars(value, max_length):
if len(value) <= max_length:
return value
truncd_val = value[:max_length]
if value[max_length] != " ":
rightmost_space = truncd_val.rfind(" ")
if rightmost_space != -1:
truncd_val = truncd_val[:rightmost_space]
return truncd_val + "..."

View file

@ -12,7 +12,7 @@ register = template.Library()
@register.simple_tag
def current_domain():
current_site = Site.objects.get_current()
return current_site and current_site.domain
return current_site and current_site.domain.replace('www', 'dev')
@register.simple_tag(takes_context=True)
def localdatetime(context, date, date_format):