Merge branch 'django1.11' into django2.0

* django1.11: (73 commits)
  Switching to new celery 4 standalone binary.
  Fixing various mongo data calls.
  Upgrading to latest celery 4 (holy moly), which required some big changes to project layout. Still needs supervisor scripts updated.
  Removing unused log on cookies.
  I believe this Context wrapping is still preserved. See this django ticket: https://code.djangoproject.com/ticket/28125. Reverting this fixes the error, so I'm assuming this is that type of render.
  Have to revert 3f122d5e03 because this broke existing sessions (logged me out) because the model has changed and the serialized model stored in redis no longer matches. Whew, this took a while to figure out.
  Upgrading redis cache.
  Adding cookies to path inspector.
  Removing dupe db log.
  Fixing missing DB logs (redis and mongo) due to this change in django 1.8: "connections.queries is now a read-only attribute."
  Removing migrations that set a default date of 2020-05-08. Not sure why this was committed. I thought we resolved the issue with default datetimes?
  Fixing CallableBool.
  Missing import
  Fixing runtime errors on django 1.10
  Fixing OAuth connect.
  Fixing various django1.9 issues, mainly around templates.
  BASE_DIR
  Not every story is from a feed.
  Styling background colors for newsletters.
  Styling more newsletter elements.
  ...
This commit is contained in:
Samuel Clay 2020-06-30 12:34:59 -04:00
commit 0d6cb69548
137 changed files with 191616 additions and 2307 deletions

23
UPGRADING.md Normal file
View file

@ -0,0 +1,23 @@
# Upgrading from Django 1.5 to 3.0
## Django 1.7
pip install -r requirements.txt
pip uninstall south
./manage.py migrate
## Django 1.8
pip install -r requirements.txt
./manage.py migrate
## Django 1.9
pip install -r requirements.txt
./manage.py migrate oauth2_provider 0001 --fake
./manage.py migrate
## Django 1.10
pip install -r requirements.txt
./manage.py migrate

View file

@ -2,6 +2,6 @@ from django.conf.urls import url
from apps.categories import views
urlpatterns = [
url(r'^/?$', views.all_categories, name='all-categories'),
url(r'^$', views.all_categories, name='all-categories'),
url(r'^subscribe/?$', views.subscribe, name='categories-subscribe'),
]

View file

@ -230,4 +230,3 @@ class UploadedOPML(mongo.Document):
'order': '-upload_date',
'indexes': ['user_id', '-upload_date'],
}

View file

@ -45,22 +45,5 @@ class ImportTest(TestCase):
# Verify user now has feeds
subs = UserSubscription.objects.filter(user=user)
self.assertEqual(subs.count(), 0)
def test_google_reader_import(self):
self.client.login(username='conesus', password='test')
user = User.objects.get(username='conesus')
f = open(os.path.join(os.path.dirname(__file__), 'fixtures/google_reader.xml'))
xml = f.read()
f.close()
reader_importer = GoogleReaderImporter(user, xml=xml)
reader_importer.import_feeds()
subs = UserSubscription.objects.filter(user=user)
self.assertEqual(subs.count(), 66)
usf = UserSubscriptionFolders.objects.get(user=user)
# print json.decode(usf.folders)
self.assertEqual(json.decode(usf.folders), [{'Tech': [4, 5, 2, 9, 10, 12, 13, 14, 20, 23, 24, 26, 27, 28, 31, 32, 33, 34, 48, 49, 62, 64]}, 1, 2, 3, 6, {'Blogs': [1, 3, 25, 29, 30, 39, 40, 41, 50, 55, 57, 58, 59, 60, 66]}, {'Blogs \u2014 Tumblrs': [5, 21, 37, 38, 53, 54, 63, 65]}, {'Blogs \u2014 The Bloglets': [6, 16, 22, 35, 51, 56]}, {'New York': [7, 8, 17, 18, 19, 36, 45, 47, 52, 61]}, {'Cooking': [11, 15, 42, 43, 46]}, 44])
self.assertEquals(subs.count(), 0)

View file

@ -63,7 +63,6 @@ def opml_upload(request):
payload = dict(folders=folders, feeds=feeds)
logging.user(request, "~FR~SBOPML Upload: ~SK%s~SN~SB~FR feeds" % (len(feeds)))
request.session['import_from_google_reader'] = False
else:
message = "Attach an .opml file."
code = -1
@ -86,3 +85,4 @@ def opml_export(request):
)
return response

View file

@ -11,7 +11,7 @@ from django.conf import settings
from mongoengine.queryset import NotUniqueError
from mongoengine.queryset import OperationError
from apps.social.models import MSocialServices, MSocialSubscription, MSharedStory
from apps.social.tasks import SyncTwitterFriends, SyncFacebookFriends, SyncAppdotnetFriends
from apps.social.tasks import SyncTwitterFriends, SyncFacebookFriends
from apps.reader.models import UserSubscription, UserSubscriptionFolders, RUserStory
from apps.analyzer.models import MClassifierTitle, MClassifierAuthor, MClassifierFeed, MClassifierTag
from apps.analyzer.models import compute_story_score
@ -23,7 +23,6 @@ from utils.view_functions import render_to
from utils import urlnorm
from utils import json_functions as json
from vendor import facebook
from vendor import appdotnet
@login_required
@render_to('social/social_connect.xhtml')
@ -31,9 +30,9 @@ def twitter_connect(request):
twitter_consumer_key = settings.TWITTER_CONSUMER_KEY
twitter_consumer_secret = settings.TWITTER_CONSUMER_SECRET
oauth_token = request.POST.get('oauth_token')
oauth_verifier = request.POST.get('oauth_verifier')
denied = request.POST.get('denied')
oauth_token = request.GET.get('oauth_token')
oauth_verifier = request.GET.get('oauth_verifier')
denied = request.GET.get('denied')
if denied:
logging.user(request, "~BB~FRDenied Twitter connect")
return {'error': 'Denied! Try connecting again.'}
@ -94,7 +93,7 @@ def facebook_connect(request):
"display": "popup",
}
verification_code = request.POST.get('code')
verification_code = request.GET.get('code')
if verification_code:
args["client_secret"] = facebook_secret
args["code"] = verification_code
@ -136,9 +135,9 @@ def facebook_connect(request):
logging.user(request, "~BB~FRFinishing Facebook connect")
return {}
elif request.POST.get('error'):
logging.user(request, "~BB~FRFailed Facebook connect, error: %s" % request.POST.get('error'))
return {'error': '%s... Try connecting again.' % request.POST.get('error')}
elif request.GET.get('error'):
logging.user(request, "~BB~FRFailed Facebook connect, error: %s" % request.GET.get('error'))
return {'error': '%s... Try connecting again.' % request.GET.get('error')}
else:
# Start the OAuth process
logging.user(request, "~BB~FRStarting Facebook connect")
@ -160,15 +159,7 @@ def facebook_disconnect(request):
social_services.disconnect_facebook()
return HttpResponseRedirect(reverse('load-user-friends'))
@ajax_login_required
def appdotnet_disconnect(request):
logging.user(request, "~BB~FRDisconnecting App.net")
social_services = MSocialServices.objects.get(user_id=request.user.pk)
social_services.disconnect_appdotnet()
return HttpResponseRedirect(reverse('load-user-friends'))
@ajax_login_required
@json.json_view
def follow_twitter_account(request):

View file

@ -1,6 +1,7 @@
#add homepage user from settings
#add popular user
from settings import HOMEPAGE_USERNAME
import datetime
from django.conf import settings
from apps.profile.models import create_profile
from django.contrib.auth.models import User
from django.core.management.base import BaseCommand
@ -8,16 +9,15 @@ from django.core.management.base import BaseCommand
class Command(BaseCommand):
def handle(self, *args, **options):
try:
user = User.objects.get(username=HOMEPAGE_USERNAME)
print(f"Found user {HOMEPAGE_USERNAME}")
except:
user = User.objects.create(username=HOMEPAGE_USERNAME)
user.save()
print(f"Created user {HOMEPAGE_USERNAME}")
try:
create_profile(None, user, None)
except:
print(f"Profile already created for user {user.username}")
print("User {0} created".format(HOMEPAGE_USERNAME))
def _create(username):
try:
User.objects.get(username=username)
print("User {0} exists".format(username))
except User.DoesNotExist:
instance = User.objects.create(username=username, last_login=datetime.datetime.now())
instance.save()
create_profile(None, instance, None)
print("User {0} created".format(username))
_create(settings.HOMEPAGE_USERNAME)
_create('popular')

View file

@ -7,7 +7,7 @@ from utils import log as logging
from django.http import HttpResponse
from django.conf import settings
from django.db import connection
from django.template import Template
from django.template import Template, Context
from apps.statistics.rstats import round_time
from utils import json_functions as json
@ -70,8 +70,8 @@ class DBProfilerMiddleware:
def process_response(self, request, response):
if hasattr(request, 'sql_times_elapsed'):
middleware = SQLLogToConsoleMiddleware()
middleware.process_celery(self)
# middleware = SQLLogToConsoleMiddleware()
# middleware.process_celery(self)
# logging.debug(" ---> ~FGProfiling~FB app: %s" % request.sql_times_elapsed)
self._save_times(request.sql_times_elapsed)
return response
@ -124,6 +124,8 @@ class SQLLogToConsoleMiddleware:
if connection.queries:
time_elapsed = sum([float(q['time']) for q in connection.queries])
queries = connection.queries
if getattr(connection, 'queriesx', False):
queries.extend(connection.queriesx)
for query in queries:
if query.get('mongo'):
query['sql'] = "~FM%s: %s" % (query['mongo']['collection'], query['mongo']['query'])
@ -137,11 +139,11 @@ class SQLLogToConsoleMiddleware:
query['sql'] = re.sub(r'DELETE', '~FR~SBDELETE', query['sql'])
t = Template("{% for sql in sqllog %}{% if not forloop.first %} {% endif %}[{{forloop.counter}}] ~FC{{sql.time}}s~FW: {{sql.sql|safe}}{% if not forloop.last %}\n{% endif %}{% endfor %}")
if settings.DEBUG:
logging.debug(t.render({
logging.debug(t.render(Context({
'sqllog': queries,
'count': len(queries),
'time': time_elapsed,
}))
})))
times_elapsed = {
'sql': sum([float(q['time'])
for q in queries if not q.get('mongo') and

View file

@ -28,9 +28,8 @@ from utils import json_functions as json
from utils.user_functions import generate_secret_token
from utils.feed_functions import chunks
from vendor.timezones.fields import TimeZoneField
from vendor.paypal.standard.ipn.signals import subscription_signup, payment_was_successful, recurring_payment
from vendor.paypal.standard.ipn.signals import payment_was_flagged
from vendor.paypal.standard.ipn.models import PayPalIPN
from paypal.standard.ipn.signals import valid_ipn_received, invalid_ipn_received
from paypal.standard.ipn.models import PayPalIPN
from vendor.paypalapi.interface import PayPalInterface
from vendor.paypalapi.exceptions import PayPalAPIResponseError
from zebra.signals import zebra_webhook_customer_subscription_created
@ -618,7 +617,7 @@ class Profile(models.Model):
now = int(datetime.datetime.now().strftime('%s'))
r.zadd(key, { -1: now })
r.expire(key, settings.SUBSCRIBER_EXPIRE*24*60*60)
r.zadd(premium_key, { -1, now })
r.zadd(premium_key, {-1: now})
r.expire(premium_key, settings.SUBSCRIBER_EXPIRE*24*60*60)
logging.info(" ---> [%-30s] ~SN~FBCounting subscribers, storing in ~SBredis~SN: ~FMt:~SB~FM%s~SN a:~SB%s~SN p:~SB%s~SN ap:~SB%s" %
@ -1078,7 +1077,7 @@ def paypal_signup(sender, **kwargs):
user.profile.activate_premium()
user.profile.cancel_premium_stripe()
user.profile.cancel_premium_paypal(second_most_recent_only=True)
subscription_signup.connect(paypal_signup)
valid_ipn_received.connect(paypal_signup)
def paypal_payment_history_sync(sender, **kwargs):
ipn_obj = sender
@ -1091,7 +1090,7 @@ def paypal_payment_history_sync(sender, **kwargs):
user.profile.setup_premium_history()
except:
return {"code": -1, "message": "User doesn't exist."}
payment_was_successful.connect(paypal_payment_history_sync)
valid_ipn_received.connect(paypal_payment_history_sync)
def paypal_payment_was_flagged(sender, **kwargs):
ipn_obj = sender
@ -1105,7 +1104,7 @@ def paypal_payment_was_flagged(sender, **kwargs):
logging.user(user, "~BC~SB~FBPaypal subscription payment flagged")
except:
return {"code": -1, "message": "User doesn't exist."}
payment_was_flagged.connect(paypal_payment_was_flagged)
invalid_ipn_received.connect(paypal_payment_was_flagged)
def paypal_recurring_payment_history_sync(sender, **kwargs):
ipn_obj = sender
@ -1118,7 +1117,7 @@ def paypal_recurring_payment_history_sync(sender, **kwargs):
user.profile.setup_premium_history()
except:
return {"code": -1, "message": "User doesn't exist."}
recurring_payment.connect(paypal_recurring_payment_history_sync)
valid_ipn_received.connect(paypal_recurring_payment_history_sync)
def stripe_signup(sender, full_json, **kwargs):
stripe_id = full_json['data']['object']['customer']

View file

@ -30,7 +30,7 @@ from utils.view_functions import render_to, is_true
from utils.user_functions import get_user
from utils import log as logging
from vendor.paypalapi.exceptions import PayPalAPIResponseError
from vendor.paypal.standard.forms import PayPalPaymentsForm
from paypal.standard.forms import PayPalPaymentsForm
SINGLE_FIELD_PREFS = ('timezone','feed_pane_size','hide_mobile','send_emails',
'hide_getting_started', 'has_setup_feeds', 'has_found_friends',
@ -684,10 +684,10 @@ def email_optout(request):
@json.json_view
def ios_subscription_status(request):
logging.debug(" ---> iOS Subscription Status: %s" % request.__dict__)
logging.debug(" ---> iOS Subscription Status: %s" % request.body)
subject = "iOS Subscription Status"
message = """%s""" % (request.__dict__)
message = """%s""" % (request.body)
mail_admins(subject, message)
return {

View file

@ -310,7 +310,7 @@ class UserSubscription(models.Model):
pipeline = rt.pipeline()
for story_hash_group in chunks(story_hashes, 100):
pipeline.zadd(ranked_stories_keys, **dict(story_hash_group))
pipeline.zadd(ranked_stories_keys, dict(story_hash_group))
pipeline.execute()
story_hashes = range_func(ranked_stories_keys, offset, limit)
@ -325,7 +325,7 @@ class UserSubscription(models.Model):
cutoff_date=cutoff_date)
if unread_story_hashes:
for unread_story_hash_group in chunks(unread_story_hashes, 100):
rt.zadd(unread_ranked_stories_keys, **dict(unread_story_hash_group))
rt.zadd(unread_ranked_stories_keys, dict(unread_story_hash_group))
unread_feed_story_hashes = range_func(unread_ranked_stories_keys, offset, limit)
rt.expire(ranked_stories_keys, 60*60)

View file

@ -112,10 +112,7 @@ def dashboard(request, **kwargs):
statistics = MStatistics.all()
social_profile = MSocialProfile.get_user(user.pk)
custom_styling = MCustomStyling.get_user(user.pk)
start_import_from_google_reader = request.session.get('import_from_google_reader', False)
if start_import_from_google_reader:
del request.session['import_from_google_reader']
preferences = json.decode(user.profile.preferences)
if not user.is_active:
url = "https://%s%s" % (Site.objects.get_current().domain,
@ -126,6 +123,7 @@ def dashboard(request, **kwargs):
return {
'user_profile' : user.profile,
'preferences' : preferences,
'feed_count' : feed_count,
'custom_styling' : custom_styling,
'account_images' : list(range(1, 4)),
@ -133,7 +131,6 @@ def dashboard(request, **kwargs):
'unmoderated_feeds' : unmoderated_feeds,
'statistics' : statistics,
'social_profile' : social_profile,
'start_import_from_google_reader': start_import_from_google_reader,
'debug' : settings.DEBUG,
}, "reader/dashboard.xhtml"
@ -262,7 +259,7 @@ def load_feeds(request):
if flat == 'false': flat = False
if flat: return load_feeds_flat(request)
platform = extract_user_agent(request)
if platform in ['iPhone', 'iPad', 'Androd']:
# Remove this check once the iOS and Android updates go out which have update_counts=False
@ -825,7 +822,10 @@ def load_single_feed(request, feed_id):
# time.sleep(random.randint(2, 7) / 10.0)
# time.sleep(random.randint(1, 10))
time.sleep(delay)
# if page == 1:
# time.sleep(1)
# else:
# time.sleep(20)
# if page == 2:
# assert False

View file

@ -28,6 +28,7 @@ from django.contrib.sites.models import Site
from django.template.defaultfilters import slugify
from django.utils.encoding import smart_bytes, smart_text
from mongoengine.queryset import OperationError, Q, NotUniqueError
from mongoengine.errors import ValidationError
from vendor.timezones.utilities import localtime_for_timezone
from apps.rss_feeds.tasks import UpdateFeeds, PushFeeds, ScheduleCountTagsForUser
from apps.rss_feeds.text_importer import TextImporter
@ -1166,7 +1167,7 @@ class Feed(models.Model):
'requesting_user_id': kwargs.get('requesting_user_id', None)
}
if getattr(settings, 'TEST_DEBUG', False):
if getattr(settings, 'TEST_DEBUG', False) and "NEWSBLUR_DIR" in self.feed_address:
print(" ---> Testing feed fetch: %s" % self.log_title)
# options['force_fp'] = True # No, why would this be needed?
original_feed_address = self.feed_address
@ -2351,11 +2352,6 @@ class MFeedPage(mongo.Document):
'allow_inheritance': False,
}
def save(self, *args, **kwargs):
if self.page_data:
self.page_data = zlib.compress(self.page_data)
return super(MFeedPage, self).save(*args, **kwargs)
def page(self):
return zlib.decompress(self.page_data)

View file

@ -289,7 +289,8 @@ class PageImporter(object):
if feed_page.page() == html:
logging.debug(' ---> [%-30s] ~FYNo change in page data: %s' % (self.feed.log_title[:30], self.feed.feed_link))
else:
feed_page.page_data = html
# logging.debug(' ---> [%-30s] ~FYChange in page data: %s (%s/%s %s/%s)' % (self.feed.log_title[:30], self.feed.feed_link, type(html), type(feed_page.page()), len(html), len(feed_page.page())))
feed_page.page_data = zlib.compress(html)
feed_page.save()
except MFeedPage.DoesNotExist:
feed_page = MFeedPage.objects.create(feed_id=self.feed.pk, page_data=html)

View file

@ -90,7 +90,7 @@ class TaskBrokenFeeds(Task):
r.zremrangebyscore('tasked_feeds', 0, hours_ago)
# r.sadd('queued_feeds', *old_tasked_feeds)
for feed_id in old_tasked_feeds:
r.zincrby('error_feeds', feed_id, 1)
r.zincrby('error_feeds', 1, feed_id)
feed = Feed.get_by_id(feed_id)
feed.set_next_scheduled_update()
logging.debug(" ---> ~SN~FBRe-queuing ~SB%s~SN dropped/broken feeds (~SB%s/%s~SN queued/tasked)" % (

View file

@ -182,7 +182,7 @@ def load_feed_statistics_embedded(request, feed_id):
'feed': feed,
}
)
def assemble_statistics(user, feed_id):
timezone = user.profile.timezone
stats = dict()

View file

@ -29,7 +29,6 @@ from apps.rss_feeds.text_importer import TextImporter
from apps.rss_feeds.page_importer import PageImporter
from apps.profile.models import Profile, MSentEmail
from vendor import facebook
from vendor import appdotnet
from vendor import pynliner
from utils import log as logging
from utils import json_functions as json
@ -43,7 +42,7 @@ from io import StringIO
try:
from apps.social.spam import detect_spammers
except ImportError:
logging.debug(" ---> ~SN~FRCouldn't find ~SBspam.py~SN.")
# logging.debug(" ---> ~SN~FRCouldn't find ~SBspam.py~SN.")
pass
RECOMMENDATIONS_LIMIT = 5
@ -1109,7 +1108,7 @@ class MSocialSubscription(mongo.Document):
pipeline = rt.pipeline()
for story_hash_group in chunks(story_hashes, 100):
pipeline.zadd(ranked_stories_keys, **dict(story_hash_group))
pipeline.zadd(ranked_stories_keys, dict(story_hash_group))
pipeline.execute()
story_hashes_and_dates = range_func(ranked_stories_keys, offset, limit, withscores=True)
if not story_hashes_and_dates:
@ -1130,7 +1129,7 @@ class MSocialSubscription(mongo.Document):
if unread_story_hashes:
pipeline = rt.pipeline()
for unread_story_hash_group in chunks(unread_story_hashes, 100):
pipeline.zadd(unread_ranked_stories_keys, **dict(unread_story_hash_group))
pipeline.zadd(unread_ranked_stories_keys, dict(unread_story_hash_group))
pipeline.execute()
unread_feed_story_hashes = range_func(unread_ranked_stories_keys, offset, limit)
@ -1870,8 +1869,8 @@ class MSharedStory(mongo.DynamicDocument):
self.feed_guid_hash : time.mktime(self.shared_date.timetuple())
}
r.zadd('zB:%s' % self.user_id, redis_data)
# r2.zadd('zB:%s' % self.user_id, self.feed_guid_hash,
# time.mktime(self.shared_date.timetuple()))
# r2.zadd('zB:%s' % self.user_id, {self.feed_guid_hash:
# time.mktime(self.shared_date.timetuple())})
r.expire('B:%s' % self.user_id, settings.DAYS_OF_STORY_HASHES*24*60*60)
# r2.expire('B:%s' % self.user_id, settings.DAYS_OF_STORY_HASHES*24*60*60)
r.expire('zB:%s' % self.user_id, settings.DAYS_OF_STORY_HASHES*24*60*60)
@ -2135,10 +2134,8 @@ class MSharedStory(mongo.DynamicDocument):
if service == 'twitter':
posted = social_service.post_to_twitter(self)
# elif service == 'facebook':
# posted = social_service.post_to_facebook(self)
elif service == 'appdotnet':
posted = social_service.post_to_appdotnet(self)
elif service == 'facebook':
posted = social_service.post_to_facebook(self)
if posted:
self.posted_to_services.append(service)
@ -2392,25 +2389,20 @@ class MSocialServices(mongo.Document):
facebook_friend_ids = mongo.ListField(mongo.StringField())
facebook_picture_url = mongo.StringField()
facebook_refresh_date = mongo.DateTimeField()
appdotnet_uid = mongo.StringField()
appdotnet_access_token= mongo.StringField()
appdotnet_friend_ids = mongo.ListField(mongo.StringField())
appdotnet_picture_url = mongo.StringField()
appdotnet_refresh_date= mongo.DateTimeField()
upload_picture_url = mongo.StringField()
syncing_twitter = mongo.BooleanField(default=False)
syncing_facebook = mongo.BooleanField(default=False)
syncing_appdotnet = mongo.BooleanField(default=False)
meta = {
'collection': 'social_services',
'indexes': ['user_id', 'twitter_friend_ids', 'facebook_friend_ids', 'twitter_uid', 'facebook_uid', 'appdotnet_uid'],
'indexes': ['user_id', 'twitter_friend_ids', 'facebook_friend_ids', 'twitter_uid', 'facebook_uid'],
'allow_inheritance': False,
'strict': False,
}
def __unicode__(self):
user = User.objects.get(pk=self.user_id)
return "%s (Twitter: %s, FB: %s, ADN: %s)" % (user.username, self.twitter_uid, self.facebook_uid, self.appdotnet_uid)
return "%s (Twitter: %s, FB: %s)" % (user.username, self.twitter_uid, self.facebook_uid)
def canonical(self):
user = User.objects.get(pk=self.user_id)
@ -2426,11 +2418,6 @@ class MSocialServices(mongo.Document):
'facebook_picture_url': self.facebook_picture_url,
'syncing': self.syncing_facebook,
},
'appdotnet': {
'appdotnet_uid': self.appdotnet_uid,
'appdotnet_picture_url': self.appdotnet_picture_url,
'syncing': self.syncing_appdotnet,
},
'gravatar': {
'gravatar_picture_url': "https://www.gravatar.com/avatar/" + \
hashlib.md5(user.email.lower().encode('utf-8')).hexdigest()
@ -2493,10 +2480,6 @@ class MSocialServices(mongo.Document):
graph = facebook.GraphAPI(access_token=self.facebook_access_token, version="3.1")
return graph
def appdotnet_api(self):
adn_api = appdotnet.Appdotnet(access_token=self.appdotnet_access_token)
return adn_api
def sync_twitter_friends(self):
user = User.objects.get(pk=self.user_id)
logging.user(user, "~BG~FMTwitter import starting...")
@ -2640,84 +2623,6 @@ class MSocialServices(mongo.Document):
return following
def sync_appdotnet_friends(self):
user = User.objects.get(pk=self.user_id)
logging.user(user, "~BG~FMApp.net import starting...")
api = self.appdotnet_api()
if not api:
logging.user(user, "~BG~FMApp.net import ~SBfailed~SN: no api access.")
self.syncing_appdotnet = False
self.save()
return
friend_ids = []
has_more_friends = True
before_id = None
since_id = None
while has_more_friends:
friends_resp = api.getUserFollowingIds(self.appdotnet_uid,
before_id=before_id,
since_id=since_id)
friends = json.decode(friends_resp)
before_id = friends['meta'].get('min_id')
since_id = friends['meta'].get('max_id')
has_more_friends = friends['meta'].get('more')
friend_ids.extend([fid for fid in friends['data']])
if not friend_ids:
logging.user(user, "~BG~FMApp.net import ~SBfailed~SN: no friend_ids.")
self.syncing_appdotnet = False
self.save()
return
adn_user = json.decode(api.getUser(self.appdotnet_uid))['data']
self.appdotnet_picture_url = adn_user['avatar_image']['url']
self.appdotnet_username = adn_user['username']
self.appdotnet_friend_ids = friend_ids
self.appdotnet_refreshed_date = datetime.datetime.utcnow()
self.syncing_appdotnet = False
self.save()
profile = MSocialProfile.get_user(self.user_id)
profile.bio = profile.bio or adn_user['description']['text']
profile.save()
profile.count_follows()
if not profile.photo_url or not profile.photo_service:
self.set_photo('appdotnet')
self.follow_appdotnet_friends()
def follow_appdotnet_friends(self):
social_profile = MSocialProfile.get_user(self.user_id)
following = []
followers = 0
if not self.autofollow:
return following
# Follow any friends already on NewsBlur
user_social_services = MSocialServices.objects.filter(appdotnet_uid__in=self.appdotnet_friend_ids)
for user_social_service in user_social_services:
followee_user_id = user_social_service.user_id
socialsub = social_profile.follow_user(followee_user_id)
if socialsub:
following.append(followee_user_id)
# Friends already on NewsBlur should follow back
# following_users = MSocialServices.objects.filter(appdotnet_friend_ids__contains=self.appdotnet_uid)
# for following_user in following_users:
# if following_user.autofollow:
# following_user_profile = MSocialProfile.get_user(following_user.user_id)
# following_user_profile.follow_user(self.user_id, check_unfollowed=True)
# followers += 1
user = User.objects.get(pk=self.user_id)
logging.user(user, "~BG~FMApp.net import: %s users, now following ~SB%s~SN with ~SB%s~SN follower-backs" % (len(self.appdotnet_friend_ids), len(following), followers))
return following
def disconnect_twitter(self):
self.syncing_twitter = False
self.twitter_uid = None
@ -2728,11 +2633,6 @@ class MSocialServices(mongo.Document):
self.facebook_uid = None
self.save()
def disconnect_appdotnet(self):
self.syncing_appdotnet = False
self.appdotnet_uid = None
self.save()
def set_photo(self, service):
profile = MSocialProfile.get_user(self.user_id)
if service == 'nothing':
@ -2859,21 +2759,6 @@ class MSocialServices(mongo.Document):
return
return True
def post_to_appdotnet(self, shared_story):
message = shared_story.generate_post_to_service_message(truncate=256)
try:
api = self.appdotnet_api()
api.createPost(text=message, links=[{
'text': shared_story.decoded_story_title,
'url': shared_story.blurblog_permalink()
}])
except Exception as e:
print(e)
return
return True
class MInteraction(mongo.Document):

View file

@ -9,7 +9,7 @@ class PostToService(Task):
def run(self, shared_story_id, service):
try:
shared_story = MSharedStory.objects.get(id=shared_story_id)
shared_story = MSharedStory.objects.get(id=ObjectId(shared_story_id))
shared_story.post_to_service(service)
except MSharedStory.DoesNotExist:
logging.debug(" ---> Shared story not found (%s). Can't post to: %s" % (shared_story_id, service))
@ -35,13 +35,13 @@ class EmailFirstShare(Task):
class EmailCommentReplies(Task):
def run(self, shared_story_id, reply_id):
shared_story = MSharedStory.objects.get(id=shared_story_id)
shared_story.send_emails_for_new_reply(reply_id)
shared_story = MSharedStory.objects.get(id=ObjectId(shared_story_id))
shared_story.send_emails_for_new_reply(ObjectId(reply_id))
class EmailStoryReshares(Task):
def run(self, shared_story_id):
shared_story = MSharedStory.objects.get(id=shared_story_id)
shared_story = MSharedStory.objects.get(id=ObjectId(shared_story_id))
shared_story.send_email_for_reshare()
class SyncTwitterFriends(Task):
@ -55,13 +55,7 @@ class SyncFacebookFriends(Task):
def run(self, user_id):
social_services = MSocialServices.objects.get(user_id=user_id)
social_services.sync_facebook_friends()
class SyncAppdotnetFriends(Task):
def run(self, user_id):
social_services = MSocialServices.objects.get(user_id=user_id)
social_services.sync_appdotnet_friends()
class SharePopularStories(Task):
name = 'share-popular-stories'

View file

@ -659,14 +659,10 @@ def mark_story_as_shared(request):
if post_to_services:
for service in post_to_services:
if service not in shared_story.posted_to_services:
if service == 'appdotnet':
# XXX TODO: Remove. Only for www->dev.
shared_story.post_to_service(service)
else:
PostToService.delay(shared_story_id=shared_story.id, service=service)
PostToService.delay(shared_story_id=str(shared_story.id), service=service)
if shared_story.source_user_id and shared_story.comments:
EmailStoryReshares.apply_async(kwargs=dict(shared_story_id=shared_story.id),
EmailStoryReshares.apply_async(kwargs=dict(shared_story_id=str(shared_story.id)),
countdown=settings.SECONDS_TO_DELAY_CELERY_EMAILS)
EmailFirstShare.apply_async(kwargs=dict(user_id=request.user.pk))
@ -816,8 +812,8 @@ def save_comment_reply(request):
story_feed_id=feed_id,
story_title=shared_story.story_title)
EmailCommentReplies.apply_async(kwargs=dict(shared_story_id=shared_story.id,
reply_id=reply.reply_id),
EmailCommentReplies.apply_async(kwargs=dict(shared_story_id=str(shared_story.id),
reply_id=str(reply.reply_id)),
countdown=settings.SECONDS_TO_DELAY_CELERY_EMAILS)
if format == 'html':

View file

@ -2,27 +2,27 @@ anyjson==0.3.3
bleach==3.1.0
beautifulsoup4==4.8.0
boto==2.43.0
celery==4.4.5
celery>=4.0,<5
chardet==3.0.4
cssutils==1.0.1
django-celery-with-redis==3.0
django-celery==3.3.1
django-celery-beat==2.0.0
django-compress==1.0.1
django-cors-middleware==1.3.1
django-extensions==2.2.9
django-mailgun==0.9.1
django-oauth-toolkit==1.2.0
django-qurl==0.1.1
django-paypal==1.0
django-redis-cache==2.1.1
django-redis-sessions==0.6.1
django-ses==0.7.1
django-timezone-field==2.0
Django==2.0
Django>=2.0,<2.1
dnspython==1.15.0
Fabric==1.14.0
gunicorn==19.7
hiredis==0.2.0
httplib2==0.18.0
httplib2==0.17.4
image==1.5.27
isodate==0.5.4
lxml==4.5.1
@ -48,10 +48,10 @@ python-gflags==3.1.0
pytz==2018.3
pyyaml==5.3.1
raven==6.10.0
redis==3.2.0
requests==2.24.0
scipy==1.4.1
sgmllib3k==1.0.0
redis==3.5.3
simplejson==3.10.0
six==1.10.0
stripe==1.43.0

View file

@ -1,5 +1,5 @@
[program:celerybeat]
command=/srv/newsblur/manage.py celery beat --schedule=/srv/newsblur/data/celerybeat-schedule.db --loglevel=INFO
command=celery -A newsblur beat --schedule=/srv/newsblur/data/celerybeat-schedule.db --loglevel=INFO
directory=/srv/newsblur
environment=PATH="/srv/newsblur/venv/newsblur/bin"
user=sclay

View file

@ -1,5 +1,5 @@
[program:celery]
command=/srv/newsblur/manage.py celery worker --loglevel=INFO -Q new_feeds,push_feeds,update_feeds
command=celery -A newsblur worker --loglevel=INFO -Q new_feeds,push_feeds,update_feeds
environment=PATH="/srv/newsblur/venv/newsblur/bin"
directory=/srv/newsblur
user=sclay

View file

@ -1,5 +1,5 @@
[program:celeryd_beat]
command=/srv/newsblur/manage.py celery worker --loglevel=INFO -Q beat_tasks -c 3
command=celery -A newsblur worker --loglevel=INFO -Q beat_tasks -c 3
directory=/srv/newsblur
environment=PATH="/srv/newsblur/venv/newsblur/bin"
user=sclay

View file

@ -1,5 +1,5 @@
[program:celeryd_beat_feeds]
command=/srv/newsblur/manage.py celery worker --loglevel=INFO -Q beat_feeds_task -c 1
command=celery -A newsblur worker --loglevel=INFO -Q beat_feeds_task -c 1
directory=/srv/newsblur
environment=PATH="/srv/newsblur/venv/newsblur/bin"
user=sclay

View file

@ -1,5 +1,5 @@
[program:celery]
command=/srv/newsblur/manage.py celery worker --loglevel=INFO -Q new_feeds,push_feeds
command=celery -A newsblur worker --loglevel=INFO -Q new_feeds,push_feeds
directory=/srv/newsblur
environment=PATH="/srv/newsblur/venv/newsblur/bin"
user=sclay

View file

@ -1,5 +1,5 @@
[program:celeryd_search_indexer]
command=/srv/newsblur/manage.py celery worker --loglevel=INFO -Q search_indexer -c 4
command=celery -A newsblur worker --loglevel=INFO -Q search_indexer -c 4
directory=/srv/newsblur
environment=PATH="/srv/newsblur/venv/newsblur/bin"
user=sclay

View file

@ -1,5 +1,5 @@
[program:celeryd_search_indexer_tasker]
command=/srv/newsblur/manage.py celery worker --loglevel=INFO -Q search_indexer_tasker -c 2
command=celery -A newsblur worker --loglevel=INFO -Q search_indexer_tasker -c 2
directory=/srv/newsblur
environment=PATH="/srv/newsblur/venv/newsblur/bin"
user=sclay

View file

@ -1,5 +1,5 @@
[program:celeryd_work_queue]
command=/srv/newsblur/manage.py celery worker --loglevel=INFO -Q work_queue
command=celery -A newsblur worker --loglevel=INFO -Q work_queue
directory=/srv/newsblur
environment=PATH="/srv/newsblur/venv/newsblur/bin"
user=sclay

2
fabfile.py vendored
View file

@ -25,7 +25,7 @@ except ImportError:
print("Digital Ocean's API not loaded. Install python-digitalocean.")
django.settings_module('settings')
django.settings_module('newsblur.settings')
try:
from django.conf import settings as django_settings
except ImportError:

View file

@ -2,14 +2,8 @@
import os
import sys
try:
import pymysql
pymysql.install_as_MySQLdb()
except:
pass
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "newsblur.settings")
from django.core.management import execute_from_command_line

View file

@ -116,7 +116,8 @@ blockquote {
.NB-spinner {
width: 16px;
height: 16px;
background: url("/media/embed/reader/recycle_spinner.gif") no-repeat 0 0;
background: transparent url('/media/embed/reader/ring_spinner.svg') no-repeat center center;
background-size: 16px;
display: none;
float: right;
margin-top: -2px;

File diff suppressed because one or more lines are too long

View file

@ -96,11 +96,13 @@ fieldset legend {
}
.NB-modal .NB-modal-loading.NB-active {
background: transparent url('/media/img/reader/recycle_spinner.gif') no-repeat 0 0;
background: transparent url('/media/embed/reader/ring_spinner.svg') no-repeat center center;
background-size: 16px;
}
.NB-loading.NB-active {
background: transparent url('/media/img/reader/spinner_ball.gif') no-repeat 0 0;
background: transparent url('/media/img/reader/worm_spinner.svg') no-repeat 0 0;
background-size: 16px;
}
.NB-modal h2,

View file

@ -15,7 +15,21 @@ body {
text-rendering: optimizeLegibility;
background-color: white;
-webkit-overflow-scrolling: touch;
scrollbar-color: #C2C2C2 #FAFAFA;
}
/*::-webkit-scrollbar {
width: 12px;
}
*/
::-webkit-scrollbar-thumb {
background-color: #C2C2C2;
}
::-webkit-scrollbar-track {
background-color: #FAFAFA;
}
.NB-layout {
overflow: hidden;
height: 100%;
@ -50,6 +64,15 @@ a img {
line-height: 12px;
}
hr {
border: none;
background-color: rgba(0, 0, 0, 0.3);
height: 1px;
width: 72%;
margin-left: auto;
margin-right: auto;
}
/* ================ */
/* = Restrictions = */
/* ================ */
@ -408,7 +431,8 @@ a img {
/* ============= */
#NB-feeds-list-loader {
background: transparent url("/media/embed/reader/big_spinner.gif?v2") no-repeat 0 0;
background: transparent url("/media/embed/reader/sun_loader_light.svg") no-repeat 0 0;
background-size: 52px;
color: rgba(0, 0, 0, .2);
font-size: 16px;
height: 51px;
@ -631,7 +655,8 @@ a img {
color: #A0A0A0;
}
.NB-feedlist .feed .NB-feed-unfetched-icon {
background: url('/media/embed/reader/recycle_spinner.gif') no-repeat 0 0;
background: transparent url('/media/embed/reader/ring_spinner.svg') no-repeat 0 0;
background-size: 16px;
opacity: .1;
width: 16px;
height: 16px;
@ -1108,12 +1133,12 @@ a img {
display: block;
float: right;
width: 20px;
height: 14px;
margin: 4px 1px;
background-image: url(/media/img/reader/mute_black.png);
height: 20px;
margin: 1px 1px 0;
background-image: url(/media/img/reader/mute_grey.png);
background-repeat: no-repeat;
background-position: center center;
background-size: 13px;
background-size: contain;
background-color: inherit;
border: none;
}
@ -2097,12 +2122,10 @@ a img {
.NB-story-title:hover:not(.NB-selected) {
background-color: #F7F7F6;
border-top: 1px solid transparent;
/* border-top: 1px solid transparent;*/
}
.NB-story-title.read:hover:not(.NB-selected) {
background-color: #FDFCFA;
/* border-top: 1px solid transparent;*/
/* border-bottom: 1px solid white;*/
}
.NB-interaction:active:not(.NB-disabled) {
@ -2164,32 +2187,32 @@ a img {
background-color: #F7F8F5;
}
.NB-layout-grid .NB-story-titles {
overflow: hidden;
display: grid;
grid-gap: 2rem;
padding: 2rem;
}
.NB-layout-grid .NB-story-content-container {
background-color: white;
}
.NB-layout-grid .NB-end-line {
margin-top: 24px;
float: left;
width: 100%;
margin: 0 -2rem -2rem;
grid-column-start: 1;
grid-column-end: -1;
}
.NB-layout-grid .NB-story-title-container {
}
.NB-layout-grid .NB-story-title-container.NB-selected {
clear: both;
overflow: hidden;
}
.NB-layout-grid .NB-story-title-container.NB-selected .NB-story-title {
margin-top: 24px;
margin: 0 -2rem;
grid-column-start: 1;
grid-column-end: -1;
}
.NB-story-grid {
margin: 3% 0 0 3%;
margin: 0;
border: 1px solid rgba(0, 0, 0, .2);
position: relative;
float: left;
width: 42%;
height: 360px;
padding: 0;
border-radius: 2px;
@ -2200,21 +2223,28 @@ a img {
.NB-story-grid.NB-story-title-hide-preview {
height: 296px;
}
.NB-layout-grid.NB-grid-columns-1 .NB-story-grid,
.NB-extra-narrow-content .NB-story-grid {
width: 94%;
.NB-layout-grid .NB-story-titles,
.NB-layout-grid.NB-grid-columns-3 .NB-story-titles,
.NB-wide-content .NB-story-titles {
grid-template-columns: none;
grid-template-columns: repeat(3, 1fr);
}
.NB-layout-grid.NB-grid-columns-2 .NB-story-grid,
.NB-narrow-content .NB-story-grid {
width: 44%;
.NB-layout-grid.NB-grid-columns-1 .NB-story-titles,
.NB-layout-grid.NB-extra-narrow-content .NB-story-titles,
.NB-extra-narrow-content.NB-narrow-content .NB-story-titles {
grid-template-columns: none;
grid-template-columns: repeat(1, 1fr);
}
.NB-layout-grid.NB-grid-columns-3 .NB-story-grid,
.NB-wide-content .NB-story-grid {
width: 29%;
.NB-layout-grid.NB-grid-columns-2 .NB-story-titles,
.NB-narrow-content .NB-story-titles {
grid-template-columns: none;
grid-template-columns: repeat(2, 1fr);
}
.NB-layout-grid.NB-grid-columns-4 .NB-story-grid,
.NB-extra-wide-content .NB-story-grid {
width: 21%;
.NB-layout-grid.NB-grid-columns-4 .NB-story-titles,
.NB-extra-wide-content .NB-story-titles {
grid-template-columns: none;
grid-template-columns: repeat(4, 1fr);
}
.NB-view-river .NB-story-grid {
padding-left: 0;
@ -2226,7 +2256,6 @@ a img {
}
.NB-story-grid.NB-selected {
clear: both;
}
.NB-story-grid .NB-storytitles-content {
@ -2244,6 +2273,7 @@ a img {
font-size: 16px;
line-height: 1.3em;
padding: 0;
word-break: break-word;
}
.NB-theme-feed-size-xs .NB-story-grid .NB-storytitles-title {
font-size: 14px;
@ -2260,6 +2290,7 @@ a img {
.NB-story-grid .NB-storytitles-content-preview {
margin: 12px 0 0 0;
word-break: break-word;
}
.NB-story-grid .NB-storytitles-story-image-container {
margin-left: 8px;
@ -2697,12 +2728,8 @@ body {
list-style: none;
background-color: white;
position: relative;
overflow: hidden;
}
.NB-feed-story .NB-feed-story-header {
padding: 3px 0 0 0;
}
.NB-feed-story.NB-river-story .NB-feed-story-header,
.NB-feed-story:first-child .NB-feed-story-header {
padding-top: 0;
@ -2746,7 +2773,8 @@ body {
rgb(55,55,55) 84%
);
padding: 2px 200px 2px 28px;
position: relative;
position: sticky;
top: 0;
border-bottom: 1px solid #000;
border-top: 1px solid #707070;
z-index: 2;
@ -3051,57 +3079,51 @@ body {
}
.NB-feed-story .NB-feed-story-tag.NB-score-1 {
/* Green */
background-color: #34912E;
background-color: #A3CA87;
color: white;
text-shadow: 0 1px 0 rgba(0, 0, 0, .7);
opacity: .4;
text-shadow: 0 1px 0 rgba(0, 0, 0, .3);
}
.NB-feed-story .NB-feed-story-tag.NB-score--1 {
/* Red */
background-color: #A90103;
background-color: #D58586;
color: white;
text-shadow: 0 1px 0 rgba(0, 0, 0, .7);
opacity: .4;
}
.NB-feed-story .NB-feed-story-tag:hover {
/* Gray, active -> [Light] Green */
background-color: #89AE6E;
text-shadow: 0 1px 0 rgba(0, 0, 0, .7);
background-color: #9CB987;
text-shadow: 0 1px 0 rgba(0, 0, 0, .3);
color: white;
opacity: .4;
}
.NB-feed-story .NB-feed-story-tag.NB-score-1:hover {
/* Green, active -> [Light] Red */
background-color: #E35356;
color: white;
text-shadow: 0 1px 0 rgba(0, 0, 0, .7);
text-shadow: 0 1px 0 rgba(0, 0, 0, .3);
}
.NB-feed-story .NB-feed-story-tag.NB-score--1:hover {
/* Red, active -> [Light] Grey */
background-color: #E2E2E2;
color: #A7A399;
text-shadow: 0 1px 0 rgba(255, 255, 255, .5);
opacity: 1;
}
.NB-feed-story .NB-feed-story-tag.NB-score-now-0:hover {
/* Grey, active */
background-color: rgba(0, 0, 0, .1);
color: #9D9A95;
text-shadow: 0 1px 0 rgba(255, 255, 255, .5);
opacity: 1;
}
.NB-feed-story .NB-feed-story-tag.NB-score-now-1.NB-score-1:hover {
/* Green, active */
background-color: #34912E;
color: white;
text-shadow: 0 1px 0 rgba(0, 0, 0, .7);
text-shadow: 0 1px 0 rgba(0, 0, 0, .3);
}
.NB-feed-story .NB-feed-story-tag.NB-score-now--1.NB-score--1:hover {
/* Red, active */
background-color: #A90103;
color: white;
text-shadow: 0 1px 0 rgba(0, 0, 0, .7);
opacity: .4;
text-shadow: 0 1px 0 rgba(0, 0, 0, .3);
}
.NB-feed-story .NB-feed-story-title .NB-score-1 {
@ -3654,8 +3676,8 @@ body {
opacity: .8;
}
.NB-story-comments-expand-icon.NB-loading {
background: transparent url("/media/embed/reader/recycle_spinner.gif") no-repeat center center;
background-size: 16px;
background: transparent url('/media/embed/reader/ring_spinner.svg') no-repeat center center;
background-size: 16px;
}
.NB-story-comments-public-teaser-wrapper:hover .NB-story-comments-expand-icon {
opacity: 1;
@ -3750,7 +3772,6 @@ body {
.NB-feed-story {
margin: 0;
clear: both;
overflow: hidden;
}
.NB-feed-stories.NB-feed-view-story .NB-feed-story {
@ -3868,7 +3889,7 @@ body {
.NB-sideoption .NB-sideoption-title {
padding: 9px 30px 8px 8px;
color: white;
color: rgba(0,0,0,0.1);
text-shadow: none;
height: 32px;
-webkit-box-sizing: border-box;
@ -4003,7 +4024,7 @@ body {
margin: 0;
}
.NB-sideoption-share {
padding: 4px 12px 6px;
padding: 9px 12px;
border: 1px solid #DBE6EA;
}
.NB-sideoption-share .NB-sideoption-share-title {
@ -4018,8 +4039,7 @@ body {
overflow: hidden;
}
.NB-sideoption-share .NB-sideoption-share-crosspost-twitter,
.NB-sideoption-share .NB-sideoption-share-crosspost-facebook,
.NB-sideoption-share .NB-sideoption-share-crosspost-appdotnet {
.NB-sideoption-share .NB-sideoption-share-crosspost-facebook {
float: left;
width: 32px;
height: 24px;
@ -4036,10 +4056,6 @@ body {
background: transparent url('/media/embed/reader/facebook_service_off.png') no-repeat center center;
background-size: 12px;
}
.NB-sideoption-share .NB-sideoption-share-crosspost-appdotnet {
background: transparent url('/media/embed/reader/appdotnet_service_off.png') no-repeat center center;
background-size: 12px;
}
.NB-sideoption-share .NB-sideoption-share-crosspost-twitter.NB-active,
.NB-sideoption-share .NB-sideoption-share-crosspost-twitter:hover {
background: transparent url('/media/embed/reader/twitter_service.png') no-repeat center center;
@ -4054,13 +4070,6 @@ body {
border-color: #6884CD;
background-color: rgba(104, 132, 205, .1);
}
.NB-sideoption-share .NB-sideoption-share-crosspost-appdotnet.NB-active,
.NB-sideoption-share .NB-sideoption-share-crosspost-appdotnet:hover {
background: transparent url('/media/embed/reader/appdotnet_service.png') no-repeat center center;
background-size: 12px;
border-color: #D16857;
background-color: rgba(209, 104, 87, .1);
}
.NB-sideoption-share .NB-sideoption-share-crosspost-text {
font-size: 9px;
text-transform: uppercase;
@ -5297,6 +5306,13 @@ form.opml_import_form input {
.NB-body-main .NB-splash-info .NB-splash-link-logo {
float: right;
display: block;
opacity: 0.4;
}
.NB-splash-blurred-logo {
background: transparent url('/media/embed/logo_newsblur_blur.png') no-repeat center center;
background-size: contain;
width: 146px;
height: 30px;
}
.NB-splash-info .NB-splash-links .NB-splash-link.NB-splash-link-logo a {
padding-top: 0;
@ -5721,7 +5737,7 @@ form.opml_import_form input {
float: right;
width: 16px;
height: 16px;
margin: 10px 0 0;
margin: 10px 8px 0;
}
.NB-add .NB-add-form .NB-add-url-submit,
.NB-add .NB-add-form .NB-add-folder-submit {
@ -5768,7 +5784,8 @@ form.opml_import_form input {
}
.NB-add input[type=text].ui-autocomplete-loading {
background: white url('/media/embed/reader/recycle_spinner.gif') no-repeat right 3px;
background: transparent url('/media/embed/reader/ring_spinner.svg') no-repeat right 3px top 3px;
background-size: 16px;
}
.chzn-drop {
@ -6027,7 +6044,7 @@ form.opml_import_form input {
}
.NB-classifiers .NB-classifier label span {
text-shadow: 1px 1px 0 rgba(255,255,255,0.5);
text-shadow: 0 1px 0 rgba(255,255,255,0.5);
}
.NB-classifiers .NB-classifier.NB-classifier-facet-disabled {
@ -6198,33 +6215,41 @@ form.opml_import_form input {
margin-right: -12px;
padding-right: 8px;
background: transparent url('/media/embed/icons/silk/control_play.png') no-repeat 4px center;
background: transparent url('/media/embed/reader/next_page.png') no-repeat 4px center;
background-size: 14px;
}
.NB-module .NB-module-next-page.NB-javascript {
opacity: .2;
}
.NB-module .NB-module-next-page:link {
background: transparent url('/media/embed/icons/silk/control_play.png') no-repeat 4px center;
background: transparent url('/media/embed/reader/next_page.png') no-repeat 4px center;
background-size: 14px;
}
.NB-module .NB-module-next-page:hover {
background: transparent url('/media/embed/icons/silk/control_play_blue.png') no-repeat 4px center;
background: transparent url('/media/embed/reader/next_page_active.png') no-repeat 4px center;
background-size: 14px;
}
.NB-module .NB-module-next-page.NB-disabled:hover {
background: transparent url('/media/embed/icons/silk/control_play.png') no-repeat 4px center;
background: transparent url('/media/embed/reader/next_page.png') no-repeat 4px center;
background-size: 14px;
cursor: default;
}
.NB-module .NB-module-previous-page {
padding-left: 4px;
background: transparent url('/media/embed/icons/silk/control_play_left.png') no-repeat 8px center;
background: transparent url('/media/embed/reader/previous_page.png') no-repeat 8px center;
background-size: 14px;
}
.NB-module .NB-module-previous-page:link {
background: transparent url('/media/embed/icons/silk/control_play_left.png') no-repeat 8px center;
background: transparent url('/media/embed/reader/previous_page.png') no-repeat 8px center;
background-size: 14px;
}
.NB-module .NB-module-previous-page:hover {
background: transparent url('/media/embed/icons/silk/control_play_left_blue.png') no-repeat 8px center;
background: transparent url('/media/embed/reader/previous_page_active.png') no-repeat 8px center;
background-size: 14px;
}
.NB-module .NB-module-previous-page.NB-disabled:hover {
background: transparent url('/media/embed/icons/silk/control_play_left.png') no-repeat 8px center;
background: transparent url('/media/embed/reader/previous_page.png') no-repeat 8px center;
background-size: 14px;
cursor: default;
}
@ -6284,7 +6309,8 @@ form.opml_import_form input {
float: right;
}
.NB-module .NB-spinner {
background: transparent url('/media/embed/reader/recycle_spinner.gif') no-repeat 0 0;
background: transparent url('/media/embed/reader/ring_spinner.svg') no-repeat center center;
background-size: 16px;
width: 16px;
height: 16px;
margin: 0 6px;
@ -7068,7 +7094,7 @@ form.opml_import_form input {
.NB-menu-manage li.NB-menu-separator {
background-color: rgba(0, 0, 0, .01);
border-bottom: 1px solid rgba(0, 0, 0, .4);
border-bottom: 1px solid rgba(0, 0, 0, .1);
padding: 0;
height: 1px;
}
@ -7502,6 +7528,16 @@ form.opml_import_form input {
background: transparent url('/media/embed/icons/circular/menu_icn_preferences.png') no-repeat 0 0;
background-size: 18px;
}
.NB-menu-manage .NB-menu-manage-theme .NB-menu-manage-image {
background: transparent url('/media/img/reader/half.png') no-repeat 0 0;
background-size: 18px;
}
.NB-menu-manage .NB-menu-manage-theme .segmented-control {
margin: 2px 0 0 36px;
}
.NB-menu-manage .NB-menu-manage-theme .segmented-control li {
padding: 4px 8px;
}
.NB-menu-manage .NB-menu-manage-account .NB-menu-manage-image {
background: transparent url('/media/embed/icons/circular/menu_icn_profile.png') no-repeat 0 0;
background-size: 18px;
@ -10222,7 +10258,8 @@ form.opml_import_form input {
}
.NB-account-payments .NB-payments-loading {
padding: 0 0 0 20px;
background: transparent url('/media/embed/reader/recycle_spinner.gif') no-repeat left 3px;
background: transparent url('/media/embed/reader/ring_spinner.svg') no-repeat left 3px top 3px;
background-size: 16px;
}
.NB-account-payments .NB-account-payment-date {
float: left;
@ -11020,7 +11057,8 @@ form.opml_import_form input {
}
.NB-friends-service.NB-friends-service-syncing .NB-friends-service-title {
padding-right: 28px;
background: transparent url('/media/embed/reader/recycle_spinner.gif') no-repeat right 3px;
background: transparent url('/media/embed/reader/ring_spinner.svg') no-repeat right 3px top 3px;
background-size: 16px;
margin-right: 8px;
}
@ -11611,7 +11649,8 @@ form.opml_import_form input {
}
.NB-interactions-popover .NB-loading {
margin: 12px auto;
background: transparent url('/media/img/reader/spinner_ball.gif') no-repeat 0 0;
background: transparent url('/media/img/reader/worm_spinner.svg') no-repeat 0 0;
background-size: 16px;
width: 16px;
height: 16px;
}
@ -12004,10 +12043,10 @@ form.opml_import_form input {
.NB-search-icon {
position: absolute;
left: 4px;
left: 6px;
top: 4px;
background: transparent url('/media/embed/reader/search_icon2.png') no-repeat 0 0;
background-size: 12px;
background: transparent url('/media/embed/reader/search_light.png') no-repeat 0 0;
background-size: 10px;
width: 12px;
height: 12px;
}
@ -12094,7 +12133,7 @@ form.opml_import_form input {
}
.NB-search-header .NB-search-header-icon {
background: transparent url('/media/embed/reader/search_icon2.png') no-repeat 0 0;
background: transparent url('/media/embed/reader/search_light.png') no-repeat 0 0;
background-size: 16px;
vertical-align: bottom;
width: 16px;
@ -12276,7 +12315,7 @@ form.opml_import_form input {
clear: both;
left: 0;
top: 0;
background: transparent url('/media/embed/reader/search_icon2.png') no-repeat 0 0;
background: transparent url('/media/embed/reader/search_light.png') no-repeat 0 0;
background-size: 16px;
vertical-align: bottom;
width: 16px;
@ -12564,7 +12603,7 @@ form.opml_import_form input {
}
.NB-module-search .NB-module-search-input.NB-active .NB-search-close {
display: block;
top: 24px;
top: 22px;
right: 16px;
}
.NB-module-search .NB-module-search-input.NB-active .NB-search-close {
@ -12593,7 +12632,8 @@ form.opml_import_form input {
}
.NB-module-search-input input.NB-active {
background: white url('/media/embed/reader/recycle_spinner.gif') no-repeat right 4px;
background: transparent url('/media/embed/reader/ring_spinner.svg') no-repeat right 4px top 4px;
background-size: 16px;
}
.NB-module-search-input label img {
width: 16px;

View file

@ -128,7 +128,13 @@
right: 0;
bottom: 0;
}
.NB-welcome-header-image img.NB-1 {
.NB-welcome-header-image img {
z-index: 0;
opacity: 0;
transition: bottom 1s ease-in-out,
opacity 1s ease-in-out;
}
.NB-welcome-header-image.NB-1 img {
height: 300px;
position: absolute;
bottom: -350px;
@ -137,10 +143,7 @@
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.NB-welcome-header-image.NB-active img.NB-1 {
bottom: 0px;
}
.NB-welcome-header-image img.NB-2 {
.NB-welcome-header-image.NB-2 img {
opacity: 0;
height: 300px;
position: absolute;
@ -151,7 +154,7 @@
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.NB-welcome-header-image img.NB-3 {
.NB-welcome-header-image.NB-3 img {
opacity: 0;
height: 300px;
position: absolute;
@ -162,6 +165,11 @@
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.NB-welcome-header-image.NB-active img {
z-index: 1;
bottom: 0;
opacity: 1;
}
.NB-welcome-header-captions {
text-align: center;
margin: 12px auto 0;
@ -443,7 +451,7 @@
/* ============ */
.NB-welcome-features {
margin: 48px 0;
margin: 64px 0;
overflow: hidden;
}
@ -457,6 +465,7 @@
.NB-welcome-features img {
width: 200px;
height: 175px;
object-fit: cover;
box-shadow: 0 0 3px rgba(30, 30, 30, 0.3);
}
.NB-welcome-features .NB-feature-caption {
@ -466,7 +475,39 @@
}
.NB-welcome-features .NB-feature-text {
color: #808080;
margin: 24px 0 0;
margin: 8px 0 0;
font-size: 15px;
line-height: 22px;
}
.NB-welcome-subfeatures {
margin: 48px 0;
overflow: hidden;
}
.NB-welcome-subfeatures .NB-feature {
float: left;
width: 46%;
margin: 0 4% 48px 0;
text-align: left;
}
.NB-welcome-subfeatures img {
float: left;
width: 120px;
height: 100px;
margin: 0 24px 0 0;
object-fit: contain;
box-shadow: 0 0 3px rgba(30, 30, 30, 0.3);
}
.NB-welcome-subfeatures .NB-feature-caption {
font-weight: bold;
font-size: 22px;
margin: 0px 0 0;
}
.NB-welcome-subfeatures .NB-feature-text {
color: #808080;
margin: 8px 0 0;
font-size: 15px;
line-height: 22px;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 408 KiB

After

Width:  |  Height:  |  Size: 408 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.0" width="64px" height="64px" viewBox="0 0 128 128" xml:space="preserve"><g><linearGradient id="linear-gradient"><stop offset="0%" stop-color="#000000" fill-opacity="1"/><stop offset="100%" stop-color="#717171" fill-opacity="0.56"/></linearGradient><linearGradient id="linear-gradient2"><stop offset="0%" stop-color="#000000" fill-opacity="1"/><stop offset="100%" stop-color="#cfcfcf" fill-opacity="0.19"/></linearGradient><path d="M64 .98A63.02 63.02 0 1 1 .98 64 63.02 63.02 0 0 1 64 .98zm0 15.76A47.26 47.26 0 1 1 16.74 64 47.26 47.26 0 0 1 64 16.74z" fill-rule="evenodd" fill="url(#linear-gradient)"/><path d="M64.12 125.54A61.54 61.54 0 1 1 125.66 64a61.54 61.54 0 0 1-61.54 61.54zm0-121.1A59.57 59.57 0 1 0 123.7 64 59.57 59.57 0 0 0 64.1 4.43zM64 115.56a51.7 51.7 0 1 1 51.7-51.7 51.7 51.7 0 0 1-51.7 51.7zM64 14.4a49.48 49.48 0 1 0 49.48 49.48A49.48 49.48 0 0 0 64 14.4z" fill-rule="evenodd" fill="url(#linear-gradient2)"/><animateTransform attributeName="transform" type="rotate" from="0 64 64" to="360 64 64" dur="1800ms" repeatCount="indefinite"></animateTransform></g></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View file

@ -0,0 +1,18 @@
<svg width="80px" height="80px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" class="lds-sunny">
<circle cx="50" cy="50" r="25" ng-attr-fill="{{config.c1}}" fill="#DBD5BA"></circle>
<g transform="rotate(60 50 50)">
<path d="M43 23L57 23L50 10Z" transform="rotate(30 50 50)" ng-attr-fill="{{config.c2}}" fill="#B28872"></path>
<path d="M43 23L57 23L50 10Z" transform="rotate(60 50 50)" ng-attr-fill="{{config.c2}}" fill="#B28872"></path>
<path d="M43 23L57 23L50 10Z" transform="rotate(90 50 50)" ng-attr-fill="{{config.c2}}" fill="#B28872"></path>
<path d="M43 23L57 23L50 10Z" transform="rotate(120 50 50)" ng-attr-fill="{{config.c2}}" fill="#B28872"></path>
<path d="M43 23L57 23L50 10Z" transform="rotate(150 50 50)" ng-attr-fill="{{config.c2}}" fill="#B28872"></path>
<path d="M43 23L57 23L50 10Z" transform="rotate(180 50 50)" ng-attr-fill="{{config.c2}}" fill="#B28872"></path>
<path d="M43 23L57 23L50 10Z" transform="rotate(210 50 50)" ng-attr-fill="{{config.c2}}" fill="#B28872"></path>
<path d="M43 23L57 23L50 10Z" transform="rotate(240 50 50)" ng-attr-fill="{{config.c2}}" fill="#B28872"></path>
<path d="M43 23L57 23L50 10Z" transform="rotate(270 50 50)" ng-attr-fill="{{config.c2}}" fill="#B28872"></path>
<path d="M43 23L57 23L50 10Z" transform="rotate(300 50 50)" ng-attr-fill="{{config.c2}}" fill="#B28872"></path>
<path d="M43 23L57 23L50 10Z" transform="rotate(330 50 50)" ng-attr-fill="{{config.c2}}" fill="#B28872"></path>
<path d="M43 23L57 23L50 10Z" transform="rotate(360 50 50)" ng-attr-fill="{{config.c2}}" fill="#B28872"></path>
<animateTransform attributeName="transform" type="rotate" calcMode="linear" values="0 50 50;180 50 50" keyTimes="0;1" dur="5.3s" begin="0s" repeatCount="indefinite"></animateTransform>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,18 @@
<svg width="80px" height="80px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" class="lds-sunny">
<circle cx="50" cy="50" r="25" ng-attr-fill="{{config.c1}}" fill="#F5F0D9"></circle>
<g transform="rotate(60 50 50)">
<path d="M43 23L57 23L50 10Z" transform="rotate(30 50 50)" ng-attr-fill="{{config.c2}}" fill="#F7E4DB"></path>
<path d="M43 23L57 23L50 10Z" transform="rotate(60 50 50)" ng-attr-fill="{{config.c2}}" fill="#F7E4DB"></path>
<path d="M43 23L57 23L50 10Z" transform="rotate(90 50 50)" ng-attr-fill="{{config.c2}}" fill="#F7E4DB"></path>
<path d="M43 23L57 23L50 10Z" transform="rotate(120 50 50)" ng-attr-fill="{{config.c2}}" fill="#F7E4DB"></path>
<path d="M43 23L57 23L50 10Z" transform="rotate(150 50 50)" ng-attr-fill="{{config.c2}}" fill="#F7E4DB"></path>
<path d="M43 23L57 23L50 10Z" transform="rotate(180 50 50)" ng-attr-fill="{{config.c2}}" fill="#F7E4DB"></path>
<path d="M43 23L57 23L50 10Z" transform="rotate(210 50 50)" ng-attr-fill="{{config.c2}}" fill="#F7E4DB"></path>
<path d="M43 23L57 23L50 10Z" transform="rotate(240 50 50)" ng-attr-fill="{{config.c2}}" fill="#F7E4DB"></path>
<path d="M43 23L57 23L50 10Z" transform="rotate(270 50 50)" ng-attr-fill="{{config.c2}}" fill="#F7E4DB"></path>
<path d="M43 23L57 23L50 10Z" transform="rotate(300 50 50)" ng-attr-fill="{{config.c2}}" fill="#F7E4DB"></path>
<path d="M43 23L57 23L50 10Z" transform="rotate(330 50 50)" ng-attr-fill="{{config.c2}}" fill="#F7E4DB"></path>
<path d="M43 23L57 23L50 10Z" transform="rotate(360 50 50)" ng-attr-fill="{{config.c2}}" fill="#F7E4DB"></path>
<animateTransform attributeName="transform" type="rotate" calcMode="linear" values="0 50 50;180 50 50" keyTimes="0;1" dur="5.3s" begin="0s" repeatCount="indefinite"></animateTransform>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.0" width="64px" height="64px" viewBox="0 0 128 128" xml:space="preserve"><g><path d="M75.4 126.63a11.43 11.43 0 0 1-2.1-22.65 40.9 40.9 0 0 0 30.5-30.6 11.4 11.4 0 1 1 22.27 4.87h.02a63.77 63.77 0 0 1-47.8 48.05v-.02a11.38 11.38 0 0 1-2.93.37z" fill="#83b4e0" fill-opacity="1"/><animateTransform attributeName="transform" type="rotate" from="0 64 64" to="360 64 64" dur="1800ms" repeatCount="indefinite"></animateTransform></g></svg>

After

Width:  |  Height:  |  Size: 620 B

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

View file

@ -1578,14 +1578,6 @@ NEWSBLUR.AssetModel = Backbone.Router.extend({
});
},
start_import_from_google_reader: function(callback) {
this.make_request('/import/import_from_google_reader/', {}, callback);
},
start_import_starred_stories_from_google_reader: function(callback) {
this.make_request('/import/import_starred_stories_from_google_reader/', {}, callback);
},
save_recommended_site: function(data, callback) {
if (NEWSBLUR.Globals.is_authenticated) {
this.make_request('/recommendations/save_recommended_feed', data, callback);

View file

@ -23,6 +23,10 @@ NEWSBLUR.Models.Story = Backbone.Model.extend({
this.public_comments = new NEWSBLUR.Collections.Comments(this.get('public_comments'));
},
feed: function() {
return NEWSBLUR.assets.get_feed(this.get('story_feed_id'));
},
score: function() {
if (NEWSBLUR.reader.flags['starred_view']) {
return 2;

View file

@ -51,7 +51,6 @@
$read_header: $('.NB-feeds-header-read'),
$tryfeed_header: $('.NB-feeds-header-tryfeed'),
$taskbar: $('.NB-taskbar-view'),
$feed_floater: $('.NB-feed-story-view-floater'),
$feedbar: $('.NB-feedbar'),
$add_button: $('.NB-task-add'),
$taskbar_options: $('.NB-taskbar-options'),
@ -61,7 +60,6 @@
'bouncing_callout': false,
'has_unfetched_feeds': false,
'count_unreads_after_import_working': false,
'import_from_google_reader_working': false,
'sidebar_closed': this.options.hide_sidebar,
'splash_page_frontmost': true
};
@ -150,6 +148,7 @@
this.setup_unfetched_feed_check();
this.switch_story_layout();
this.load_delayed_stylesheets();
this.load_theme();
},
// ========
@ -234,6 +233,7 @@
} else {
center = NEWSBLUR.reader.layout.rightLayout.panes.center;
}
if (center) {
var center_width = center.width();
var narrow = center_width < 780;
@ -3263,6 +3263,67 @@
}
},
switch_theme: function(theme) {
this.model.preference('theme', theme);
this.load_theme();
},
load_theme: function() {
var theme = this.model.preference('theme');
var is_auto = theme == 'auto';
if (is_auto) {
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
// dark mode
theme = "dark";
} else {
theme = "light";
}
}
if (!this.flags.watching_system_theme && window.matchMedia) {
var darkMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
try {
// Chrome & Firefox
darkMediaQuery.addEventListener('change', _.bind(function(e) {
console.log(['Chrome/FF switching themes', e]);
this.load_theme();
}, this));
} catch (e1) {
try {
// Safari
darkMediaQuery.addListener(_.bind(function(e) {
console.log(['Safari switching themes', e]);
this.load_theme();
}, this));
} catch (e2) {
console.error(e2);
}
}
this.flags.watching_system_theme = true;
}
$('.NB-theme-option').removeClass('NB-active');
if (is_auto) {
$('.NB-options-theme-auto').addClass('NB-active');
} else {
$('.NB-options-theme-'+theme).addClass('NB-active');
}
$("body").addClass('NB-theme-transitioning');
if (theme == 'dark') {
$("body").addClass('NB-dark');
} else {
$("body").removeClass('NB-dark');
}
_.delay(function() {
$("body").removeClass("NB-theme-transitioning");
}, 2000);
},
close_interactions_popover: function() {
NEWSBLUR.InteractionsPopover.close();
},
@ -3407,9 +3468,30 @@
$.make('li', { className: 'NB-menu-item NB-menu-manage-preferences' }, [
$.make('div', { className: 'NB-menu-manage-image' }),
$.make('div', { className: 'NB-menu-manage-title' }, 'Preferences')
]),
$.make('li', { className: 'NB-menu-item NB-menu-manage-theme' }, [
$.make('div', { className: 'NB-menu-manage-image' }),
$.make('ul', { className: 'segmented-control NB-options-theme' }, [
$.make('li', { className: 'NB-theme-option NB-options-theme-light' }, [
$.make('div', { className: 'NB-icon' }),
'Light'
]),
$.make('li', { className: 'NB-theme-option NB-options-theme-dark' }, [
$.make('div', { className: 'NB-icon' }),
'Dark'
]),
$.make('li', { className: 'NB-theme-option NB-options-theme-auto' }, [
$.make('div', { className: 'NB-icon' }),
'Auto'
])
])
])
]);
$manage_menu.addClass('NB-menu-manage-notop');
var theme = this.model.preference('theme');
$(".NB-options-theme-light", $manage_menu).toggleClass('NB-active', theme == 'light');
$(".NB-options-theme-dark", $manage_menu).toggleClass('NB-active', theme == 'dark');
$(".NB-options-theme-auto", $manage_menu).toggleClass('NB-active', theme == 'auto');
} else if (type == 'feed') {
var feed = this.model.get_feed(feed_id);
if (!feed) return;
@ -6226,6 +6308,21 @@
});
}
});
$.targetIs(e, { tagSelector: '.NB-menu-manage-theme' }, function($t, $p){
e.preventDefault();
});
$.targetIs(e, { tagSelector: '.NB-options-theme-light' }, function($t, $p){
e.preventDefault();
self.switch_theme('light');
});
$.targetIs(e, { tagSelector: '.NB-options-theme-dark' }, function($t, $p){
e.preventDefault();
self.switch_theme('dark');
});
$.targetIs(e, { tagSelector: '.NB-options-theme-auto' }, function($t, $p){
e.preventDefault();
self.switch_theme('auto');
});
$.targetIs(e, { tagSelector: '.NB-menu-manage-logout' }, function($t, $p){
e.preventDefault();
e.stopPropagation();

View file

@ -156,7 +156,7 @@ _.extend(NEWSBLUR.ReaderFriends.prototype, {
$.make('div', { className: "NB-module-search-input NB-module-search-people" }, [
$.make('div', { className: "NB-search-close" }),
$.make('label', { 'for': "NB-friends-search-input" }, [
$.make('img', { src: NEWSBLUR.Globals.MEDIA_URL + "img/reader/search_icon2.png" })
$.make('img', { src: NEWSBLUR.Globals.MEDIA_URL + "img/reader/search_light.png" })
]),
$.make('input', { id: "NB-friends-search-input", className: 'NB-input', placeholder: "Username or email..." })
]),

View file

@ -43,7 +43,7 @@ _.extend(NEWSBLUR.ReaderGoodies.prototype, {
$.make('a', {
className: 'NB-goodies-mobile-link NB-modal-submit-button NB-modal-submit-green',
href: 'https://userstyles.org/styles/124890/newsblur-dark-theme-by-splike'
}, 'Download the Dark Theme'),
}, 'Download an alternate Dark Theme'),
$.make('div', { className: 'NB-goodies-title' }, 'Dark theme for the web'),
$.make('div', { className: 'NB-goodies-subtitle' }, [
'Use the Stylus browser extension to install a user-contributed dark theme. Note that you should use the Stylus extension and not the Stylish extension due to privacy concerns. ',

View file

@ -4,7 +4,7 @@ NEWSBLUR.ReaderIntro = function(options) {
};
var intro_page = NEWSBLUR.assets.preference('intro_page');
_.bindAll(this, 'close', 'start_import_from_google_reader', 'post_connect');
_.bindAll(this, 'close', 'post_connect');
this.options = $.extend({
'page_number': intro_page && _.isNumber(intro_page) && intro_page <= 4 ? intro_page : 1
}, defaults, options);
@ -537,82 +537,6 @@ _.extend(NEWSBLUR.ReaderIntro.prototype, {
// = Import =
// ==========
google_reader_connect: function(options) {
options = options || {};
var window_options = "location=0,status=0,width=800,height=500";
var url = "/import/authorize";
this.flags.starred_only = !!options.starred_only;
$(".NB-page-2-importing", this.$modal).text(options.starred_only ?
"Importing your sites and stories..." :
"Importing your sites...");
this.connect_window = window.open(url, '_blank', window_options);
clearInterval(this.connect_window_timer);
this.connect_window_timer = setInterval(_.bind(function() {
console.log(["post connect window?", this.connect_window, this.connect_window.closed, this.connect_window.location]);
try {
if (!this.connect_window ||
!this.connect_window.location ||
this.connect_window.closed) {
this.start_import_from_google_reader(options);
}
} catch (err) {
this.start_import_from_google_reader(options);
}
}, this), 1000);
NEWSBLUR.reader.flags.importing_from_google_reader = true;
},
start_import_from_google_reader: function(data) {
console.log(["start_import_from_google_reader", data]);
clearInterval(this.connect_window_timer);
data.starred_only = this.flags.starred_only;
var $error = $('.NB-intro-import-google .NB-error', this.$modal);
var $loading = $('.NB-intro-imports-progress .NB-loading', this.$modal);
if (data && data.error) {
$error.show().text(data.error);
this.advance_import_carousel(0);
} else {
$error.hide();
NEWSBLUR.reader.flags.importing_from_google_reader = false;
this.advance_import_carousel(1, data);
$loading.addClass('NB-active');
if (data && data.starred_only) {
NEWSBLUR.assets.start_import_starred_stories_from_google_reader(
_.bind(this.finish_import_from_google_reader, this));
} else {
NEWSBLUR.assets.start_import_from_google_reader(
_.bind(this.finish_import_from_google_reader, this));
}
}
},
finish_import_from_google_reader: function(data) {
var self = this;
console.log(["finish_import_from_google_reader", data]);
var $loading = $('.NB-intro-imports-progress .NB-loading', this.$modal);
NEWSBLUR.assets.load_feeds(_.bind(function() {
$loading.removeClass('NB-active');
this.advance_import_carousel(2);
if (data.delayed) {
NEWSBLUR.reader.flags.delayed_import = true;
self.count_feeds({fake_count: data.feed_count});
$('.NB-intro-import-delayed', self.$modal).show();
$('.NB-intro-import-restart', self.$modal).hide();
$('.NB-intro-import-message', self.$modal).hide();
} else if (data.code < 0) {
$('.NB-intro-import-delayed', self.$modal).hide();
$('.NB-intro-import-restart', self.$modal).hide();
$('.NB-intro-import-message', self.$modal).show().addClass('NB-error').text(data.message);
} else {
$('.NB-intro-import-delayed', self.$modal).hide();
$('.NB-intro-import-restart', self.$modal).show();
$('.NB-intro-import-message', self.$modal).show().removeClass('NB-error').text("All done!");
}
}, this));
},
handle_opml_upload: function() {
var self = this;
var $loading = $('.NB-intro-imports-progress .NB-loading', this.$modal);
@ -746,10 +670,6 @@ _.extend(NEWSBLUR.ReaderIntro.prototype, {
self.close_and_load_newsblur_blog();
});
$.targetIs(e, { tagSelector: '.NB-google-reader-oauth' }, function($t, $p) {
e.preventDefault();
self.google_reader_connect();
});
$.targetIs(e, { tagSelector: '.NB-starredimport-button' }, function($t, $p) {
e.preventDefault();
// self.google_reader_connect({'starred_only': true});

View file

@ -180,7 +180,7 @@ _.extend(NEWSBLUR.ReaderSendEmail.prototype, {
'%0D%0A%0D%0A--%0D%0A%0D%0A',
this.story.story_permalink,
'%0D%0A%0D%0A',
$(this.story.story_content).text(),
encodeURIComponent($(this.story.story_content()).text()),
'%0D%0A%0D%0A',
'--',
'%0D%0A%0D%0A',

View file

@ -324,8 +324,8 @@ _.extend(NEWSBLUR.ReaderStatistics.prototype, {
$plot.attr('height', height);
var myLine = new Chart($plot.get(0).getContext("2d")).Line(points, {
scaleLabel : "<%= Math.round(value) %>",
showTooltips: false,
scaleBeginAtZero: true
showTooltips: false,
scaleBeginAtZero: true
});
},
@ -336,7 +336,13 @@ _.extend(NEWSBLUR.ReaderStatistics.prototype, {
_.map(_.range(24), function(hour) {
var count = data.story_hours_history[hour] || 0;
var opacity = 1 - (count * 1.0 / max_count);
return $.make('td', { style: "background-color: rgba(255, 255, 255, " + opacity + ");" });
var theme = NEWSBLUR.assets.preference('theme');
if (theme == 'light') {
return $.make('td', { style: "background-color: rgba(255, 255, 255, " + opacity + ");" });
} else if (theme == 'dark') {
opacity = 1 - opacity;
return $.make('td', { style: "background-color: rgba(151, 187, 205, " + opacity + ");" });
}
})
]),
$.make('tr', { className: 'NB-statistics-history-chart-hours-text-row' }, [

View file

@ -6,8 +6,6 @@ NEWSBLUR.utils = {
return 'Twitter';
case 'facebook':
return 'Facebook';
case 'appdotnet':
return 'App.net';
}
},

View file

@ -53,7 +53,7 @@ NEWSBLUR.Views.SocialProfileBadge = Backbone.View.extend({
$.make('div', { className: 'NB-profile-badge-bio' }, profile.get('bio')),
(_.isNumber(profile.get('shared_stories_count')) &&
$.make('div', { className: 'NB-profile-badge-stats' }, [
$.make('span', { className: 'NB-count' }, profile.get('shared_stories_count')),
$.make('span', { className: 'NB-count' }, Inflector.commas(profile.get('shared_stories_count'))),
'shared ',
Inflector.pluralize('story', profile.get('shared_stories_count')),
' &middot; ',

View file

@ -146,6 +146,7 @@ NEWSBLUR.Views.StoryDetailView = Backbone.View.extend({
render_header: function(model, value, options) {
var params = this.get_render_params();
this.$('.NB-feed-story-header-feed').remove();
this.$('.NB-feed-story-header').replaceWith($(this.story_header_template(params)));
this.generate_gradients();
},
@ -171,15 +172,15 @@ NEWSBLUR.Views.StoryDetailView = Backbone.View.extend({
},
story_header_template: _.template('\
<div class="NB-feed-story-header-feed">\
<% if (feed) { %>\
<div class="NB-feed-story-feed">\
<img class="feed_favicon" src="<%= $.favicon(feed) %>">\
<span class="NB-feed-story-header-title"><%= feed.get("feed_title") %></span>\
</div>\
<% } %>\
</div>\
<div class="NB-feed-story-header">\
<div class="NB-feed-story-header-feed">\
<% if (feed) { %>\
<div class="NB-feed-story-feed">\
<img class="feed_favicon" src="<%= $.favicon(feed) %>">\
<span class="NB-feed-story-header-title"><%= feed.get("feed_title") %></span>\
</div>\
<% } %>\
</div>\
<div class="NB-feed-story-header-info">\
<div class="NB-feed-story-title-container">\
<div class="NB-feed-story-sentiment"></div>\
@ -229,7 +230,7 @@ NEWSBLUR.Views.StoryDetailView = Backbone.View.extend({
<div class="NB-feed-story-shares-container"></div>\
<div class="NB-story-content-container">\
<div class="NB-story-content-wrapper <% if (truncatable) { %>NB-story-content-truncatable<% } %>">\
<div class="NB-feed-story-content">\
<div class="NB-feed-story-content <% if (feed && feed.get("is_newsletter")) { %>NB-newsletter<% } %>">\
<% if (!options.skip_content) { %>\
<%= story.story_content() %>\
<% } %>\

View file

@ -73,7 +73,6 @@ NEWSBLUR.Views.StoryListView = Backbone.View.extend({
this.$el.html(_.pluck(stories, 'el'));
_.invoke(stories, 'attach_handlers');
}
this.show_correct_feed_in_feed_title_floater();
this.stories = stories;
_.defer(this.check_feed_view_scrolled_to_bottom);
this.end_loading();
@ -286,44 +285,6 @@ NEWSBLUR.Views.StoryListView = Backbone.View.extend({
this.$el.append($end_stories_line);
},
show_correct_feed_in_feed_title_floater: function(story, hide) {
var $story, $header;
var $feed_floater = NEWSBLUR.reader.$s.$feed_floater;
story = story || NEWSBLUR.reader.active_story;
if (!hide && story && story.get('story_feed_id') &&
this.cache.feed_title_floater_feed_id != story.get('story_feed_id')) {
var $story = story.story_view.$el;
$header = $('.NB-feed-story-header-feed', $story);
var $new_header = $header.clone();
if (this.feed_title_floater) this.feed_title_floater.remove();
this.feed_title_floater = new NEWSBLUR.Views.StoryDetailView({
feed_floater: true,
model: story,
el: $new_header
});
$feed_floater.html($new_header);
this.cache.feed_title_floater_feed_id = story.get('story_feed_id');
var feed = NEWSBLUR.assets.get_feed(story.get('story_feed_id'));
$feed_floater.toggleClass('NB-inverse', feed && feed.is_light());
$feed_floater.width($header.outerWidth());
} else if (hide || !story || !story.get('story_feed_id')) {
if (this.feed_title_floater) this.feed_title_floater.remove();
this.cache.feed_title_floater_feed_id = null;
}
if (story && this.cache.feed_title_floater_story_id != story.id) {
$story = $story || story.story_view.$el;
$header = $header || $('.NB-feed-story-header-feed', $story);
$('.NB-floater').removeClass('NB-floater');
$header.addClass('NB-floater');
this.cache.feed_title_floater_story_id = story.id;
} else if (!story) {
this.cache.feed_title_floater_story_id = null;
}
},
show_stories_preference_in_feed_view: function(is_creating) {
if (NEWSBLUR.reader.active_story &&
NEWSBLUR.assets.preference('feed_view_single_story')) {
@ -743,9 +704,6 @@ NEWSBLUR.Views.StoryListView = Backbone.View.extend({
var closest = $.closest(from_top - offset, positions);
story = this.cache.feed_view_story_positions[positions[closest]];
}
var hide = offset && (from_top < offset);
this.show_correct_feed_in_feed_title_floater(story, hide);
}
}

View file

@ -96,7 +96,7 @@ NEWSBLUR.StoryOptionsPopover = NEWSBLUR.ReaderPopover.extend({
]),
$.make('div', { className: 'NB-popover-section' }, [
$.make('div', { className: 'NB-popover-section-title' }, 'Font Family'),
$.make('ul', { className: 'segmented-control-vertical NB-options-font-family' }, [
$.make('ul', { className: 'segmented-control segmented-control-vertical NB-options-font-family' }, [
$.make('li', { className: 'NB-font-family-option NB-options-font-family-sans-serif NB-active' }, 'Helvetica'),
$.make('li', { className: 'NB-font-family-option NB-options-font-family-serif' }, 'Palatino / Georgia'),
$.make('li', { className: 'NB-font-family-option NB-premium-only NB-options-font-family-gotham' }, [

View file

@ -6,7 +6,6 @@ NEWSBLUR.Views.StoryShareView = Backbone.View.extend({
"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",
"click .NB-sideoption-share-crosspost-appdotnet" : "toggle_appdotnet",
"keypress .NB-sideoption-share-comments" : "autosize",
"keyup .NB-sideoption-share-comments" : "update_share_button_label",
"keydown .NB-sideoption-share-comments" : "maybe_close"
@ -35,8 +34,7 @@ NEWSBLUR.Views.StoryShareView = Backbone.View.extend({
<textarea class="NB-sideoption-share-comments"><%= story.get("shared_comments") %></textarea>\
<% if (!profile.get("private") && \
((social_services.twitter && social_services.twitter.twitter_uid) || \
(social_services.facebook && social_services.facebook.facebook_uid) || \
(social_services.facebook && social_services.facebook.appdotnet_uid))) { %>\
(social_services.facebook && social_services.facebook.facebook_uid))) { %>\
<div class="NB-sideoption-share-crosspost">\
<% _.each(social_services, function(service, service_name) { %>\
<% if (service[service_name+"_uid"]) { %>\
@ -64,7 +62,6 @@ NEWSBLUR.Views.StoryShareView = Backbone.View.extend({
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');
var $appdotnet_button = this.$('.NB-sideoption-share-crosspost-appdotnet');
if (options.close ||
($sideoption.hasClass('NB-active') && !options.resize_open)) {
@ -82,7 +79,6 @@ NEWSBLUR.Views.StoryShareView = Backbone.View.extend({
$unshare_button.toggleClass('NB-hidden', !this.model.get("shared"));
$twitter_button.removeClass('NB-active');
$facebook_button.removeClass('NB-active');
$appdotnet_button.removeClass('NB-active');
this.update_share_button_label();
this.$('textarea').autosize();
@ -220,7 +216,6 @@ NEWSBLUR.Views.StoryShareView = Backbone.View.extend({
var $share_menu = $share_button_menu.closest('.NB-sideoption-share');
var $twitter_button = this.$('.NB-sideoption-share-crosspost-twitter');
var $facebook_button = this.$('.NB-sideoption-share-crosspost-facebook');
var $appdotnet_button = this.$('.NB-sideoption-share-crosspost-appdotnet');
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());
@ -236,7 +231,6 @@ NEWSBLUR.Views.StoryShareView = Backbone.View.extend({
var post_to_services = _.compact([
$twitter_button.hasClass('NB-active') && 'twitter',
$facebook_button.hasClass('NB-active') && 'facebook',
$appdotnet_button.hasClass('NB-active') && 'appdotnet'
]);
$share_button.addClass('NB-saving').addClass('NB-disabled').text('Sharing...');
@ -394,13 +388,7 @@ NEWSBLUR.Views.StoryShareView = Backbone.View.extend({
$facebook_button.toggleClass('NB-active', !$facebook_button.hasClass('NB-active'));
},
toggle_appdotnet: function() {
var $appdotnet_button = this.$('.NB-sideoption-share-crosspost-appdotnet');
$appdotnet_button.toggleClass('NB-active', !$appdotnet_button.hasClass('NB-active'));
},
maybe_close: function(e) {
if (e.which == 27) {
e.preventDefault();

View file

@ -191,9 +191,11 @@ NEWSBLUR.Views.StoryTitleView = Backbone.View.extend({
// console.log(['render_intelligence', score, unread_view, this.model.get('visible'), this.model.get('story_title')]);
if (score >= unread_view) {
this.$el.removeClass('NB-hidden');
this.$st.removeClass('NB-hidden');
this.model.set('visible', true);
} else {
this.$el.addClass('NB-hidden');
this.$st.addClass('NB-hidden');
this.model.set('visible', false);
}

View file

@ -188,7 +188,7 @@ NEWSBLUR.Views.StoryTitlesView = Backbone.View.extend({
this.$('.NB-end-line').remove();
var $endline = $.make('div', { className: "NB-end-line NB-short" });
$endline.css({'background': '#FFF'});
$story_titles.append($endline);
this.$el.append($endline);
$endline.animate({'backgroundColor': '#E1EBFF'}, {'duration': 550, 'easing': 'easeInQuad'})
.animate({'backgroundColor': '#5C89C9'}, {'duration': 1550, 'easing': 'easeOutQuad'})
@ -323,7 +323,7 @@ NEWSBLUR.Views.StoryTitlesView = Backbone.View.extend({
position = scroll+container;
}
if (story_layout == 'grid') {
position += 21;
// position -= 21;
}
// console.log(["scroll_to_selected_story 3", position]);

View file

@ -8,12 +8,14 @@ NEWSBLUR.Welcome = Backbone.View.extend({
"click .NB-button-login" : "show_signin_form",
"click .NB-button-tryout" : "show_tryout",
"click .NB-welcome-header-caption" : "click_header_caption",
"focus input" : "stop_rotation",
"mouseenter .NB-welcome-header-caption" : "enter_header_caption",
"mouseleave .NB-welcome-header-caption" : "leave_header_caption"
},
initialize: function() {
this.start_rotation();
this.debug_password_autocomplete();
NEWSBLUR.reader.$s.$layout.hide();
},
@ -74,28 +76,36 @@ NEWSBLUR.Welcome = Backbone.View.extend({
this.rotation += 1;
}
var $images = $('.NB-welcome-header-image img').add('.NB-welcome-header-account');
var $images = $('.NB-welcome-header-image').add('.NB-welcome-header-account');
var $captions = $('.NB-welcome-header-caption');
var $in_img = $images.eq(r);
var $out_img = $images.not($in_img);
var $in_caption = $captions.eq(r);
var $out_caption = $captions.not($in_caption);
$out_img.css({zIndex: 0}).stop(true).animate({
bottom: -300,
opacity: 0
}, {easing: 'easeInOutQuart', queue: false, duration: force ? 650 : 1400, complete: callback});
$in_img.css({zIndex: 1}).stop(true).animate({
bottom: 0,
opacity: 1
}, {easing: 'easeInOutQuart', queue: false, duration: force ? 650 : 1400});
$out_img.removeClass('NB-active');
$in_img.addClass('NB-active');
// $out_img.css({zIndex: 0}).stop(true).animate({
// bottom: -300,
// opacity: 0
// }, {easing: 'easeInOutQuart', queue: false, duration: force ? 650 : 1400, complete: callback});
// $in_img.css({zIndex: 1}).stop(true).animate({
// bottom: 0,
// opacity: 1
// }, {easing: 'easeInOutQuart', queue: false, duration: force ? 650 : 1400});
$out_caption.removeClass('NB-active');
$in_caption.addClass('NB-active');
callback && callback();
if (r < 3) {
this.$('input').blur();
}
},
stop_rotation: function() {
this.flags.on_signin = true;
},
show_signin_form: function() {
var open = !NEWSBLUR.reader.flags['sidebar_closed'];
this.hide_tryout();
@ -153,6 +163,10 @@ NEWSBLUR.Welcome = Backbone.View.extend({
});
this.$('.NB-welcome-container').removeClass('NB-welcome-tryout');
},
debug_password_autocomplete: function() {
this.$("input[name=login-username]").focus();
}
});

7
newsblur/__init__.py Normal file
View file

@ -0,0 +1,7 @@
from __future__ import absolute_import, unicode_literals
# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celeryapp import app as celery_app
__all__ = ['celery_app']

17
newsblur/celeryapp.py Normal file
View file

@ -0,0 +1,17 @@
from __future__ import absolute_import, unicode_literals
import os
from celery import Celery
# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'newsblur.settings')
app = Celery('newsblur')
# Using a string here means the worker doesn't have to serialize
# the configuration object to child processes.
# - namespace='CELERY' means all celery-related configuration keys
# should have a `CELERY_` prefix.
app.config_from_object('django.conf:settings', namespace='CELERY')
# Load task modules from all registered Django app configs.
app.autodiscover_tasks()

View file

@ -4,15 +4,16 @@ import os
# ===========================
# = Directory Declaractions =
# ===========================
BASE_DIR = os.path.dirname(__file__)
NEWSBLUR_DIR = BASE_DIR
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
UTILS_ROOT = os.path.join(BASE_DIR, 'utils')
VENDOR_ROOT = os.path.join(BASE_DIR, 'vendor')
LOG_FILE = os.path.join(BASE_DIR, 'logs/newsblur.log')
IMAGE_MASK = os.path.join(BASE_DIR, 'media/img/mask.png')
LOG_PATH = os.path.join(BASE_DIR, 'log')
ROOT_DIR = os.path.dirname(__file__)
NEWSBLUR_DIR = os.path.join(ROOT_DIR, "../")
MEDIA_ROOT = os.path.join(NEWSBLUR_DIR, 'media')
STATIC_ROOT = os.path.join(NEWSBLUR_DIR, 'static')
UTILS_ROOT = os.path.join(NEWSBLUR_DIR, 'utils')
VENDOR_ROOT = os.path.join(NEWSBLUR_DIR, 'vendor')
LOG_FILE = os.path.join(NEWSBLUR_DIR, 'logs/newsblur.log')
IMAGE_MASK = os.path.join(NEWSBLUR_DIR, 'media/img/mask.png')
# ==============
# = PYTHONPATH =
# ==============
@ -70,8 +71,8 @@ USE_I18N = False
LOGIN_REDIRECT_URL = '/'
LOGIN_URL = '/account/login'
MEDIA_URL = '/media/'
STATIC_URL = '/static/'
STATIC_ROOT = '/static/'
STATIC_URL = '/media/'
STATIC_ROOT = '/media/'
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
# trailing slash.
# Examples: "http://foo.com/media/", "/media/".
@ -93,7 +94,12 @@ PAYPAL_TEST = False
# ===========================
MIDDLEWARE = [
MIDDLEWARE_CLASSES = (
'django.middleware.gzip.GZipMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'apps.profile.middleware.TimingMiddleware',
'apps.profile.middleware.LastSeenMiddleware',
'apps.profile.middleware.UserAgentBanMiddleware',
@ -107,19 +113,12 @@ MIDDLEWARE = [
'apps.profile.middleware.SQLLogToConsoleMiddleware',
'utils.mongo_raw_log_middleware.MongoDumpMiddleware',
'utils.redis_raw_log_middleware.RedisDumpMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware'
]
)
AUTHENTICATION_BACKENDS = [
AUTHENTICATION_BACKENDS = (
'oauth2_provider.backends.OAuth2Backend',
'django.contrib.auth.backends.AllowAllUsersModelBackend'
]
'django.contrib.auth.backends.ModelBackend',
)
CORS_ORIGIN_ALLOW_ALL = True
# CORS_ORIGIN_REGEX_WHITELIST = ('^(https?://)?(\w+\.)?newsblur\.com$', )
@ -176,7 +175,7 @@ LOGGING = {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
'filename': LOG_FILE,
'maxBytes': 16777216, # 16megabytes
'maxBytes': '16777216', # 16megabytes
'formatter': 'verbose'
},
'mail_admins': {
@ -258,16 +257,18 @@ DAYS_OF_STORY_HASHES = 30
SUBSCRIBER_EXPIRE = 7
ROOT_URLCONF = 'urls'
ROOT_URLCONF = 'newsblur.urls'
INTERNAL_IPS = ('127.0.0.1',)
LOGGING_LOG_SQL = True
APPEND_SLASH = False
SESSION_ENGINE = 'redis_sessions.session'
TEST_RUNNER = "utils.testrunner.TestRunner"
SESSION_COOKIE_NAME = 'newsblur_sessionid'
SESSION_COOKIE_AGE = 60*60*24*365 # 1 year
SESSION_COOKIE_AGE = 60*60*24*365*10 # 10 years
SESSION_COOKIE_DOMAIN = '.newsblur.com'
SESSION_COOKIE_HTTPONLY = False
SENTRY_DSN = 'https://XXXNEWSBLURXXX@app.getsentry.com/99999999'
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
if DEBUG:
# EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
@ -280,8 +281,8 @@ else:
# ==============
SUBDOMAIN_URLCONFS = {
None: 'urls',
'www': 'urls',
None: 'newsblur.urls',
'www': 'newsblur.urls',
}
REMOVE_WWW_FROM_DOMAIN = True
@ -297,7 +298,6 @@ LOG_TO_STREAM = False
# ===============
OAUTH2_PROVIDER_APPLICATION_MODEL = 'oauth2_provider.Application'
AUTH_USER_MODEL = 'auth.User'
INSTALLED_APPS = (
'django.contrib.auth',
@ -306,10 +306,7 @@ INSTALLED_APPS = (
'django.contrib.sites',
'django.contrib.admin',
'django_extensions',
'django.contrib.staticfiles',
'djcelery',
# 'kombu.transport.django',
'vendor.paypal.standard.ipn',
'paypal.standard.ipn',
'apps.rss_feeds',
'apps.reader',
'apps.analyzer',
@ -325,6 +322,7 @@ INSTALLED_APPS = (
'apps.oauth',
'apps.search',
'apps.categories',
'django_celery_beat',
'utils', # missing models so no migrations
'vendor',
'vendor.typogrify',
@ -345,12 +343,7 @@ ZEBRA_ENABLE_APP = True
# = Celery =
# ==========
import djcelery
djcelery.setup_loader()
# from celery import Celery
# celeryapp = Celery()
# celeryapp.config_from_object('django.conf:settings')
CELERY_ROUTES = {
CELERY_TASK_ROUTES = {
"work-queue": {
"queue": "work_queue",
"binding_key": "work_queue"
@ -380,7 +373,7 @@ CELERY_ROUTES = {
"binding_key": "search_indexer_tasker"
},
}
CELERY_QUEUES = {
CELERY_TASK_QUEUES = {
"work_queue": {
"exchange": "work_queue",
"exchange_type": "direct",
@ -422,9 +415,9 @@ CELERY_QUEUES = {
"binding_key": "search_indexer_tasker"
},
}
CELERY_DEFAULT_QUEUE = "work_queue"
CELERY_TASK_DEFAULT_QUEUE = "work_queue"
CELERYD_PREFETCH_MULTIPLIER = 1
CELERY_WORKER_PREFETCH_MULTIPLIER = 1
CELERY_IMPORTS = ("apps.rss_feeds.tasks",
"apps.social.tasks",
"apps.reader.tasks",
@ -432,15 +425,15 @@ CELERY_IMPORTS = ("apps.rss_feeds.tasks",
"apps.feed_import.tasks",
"apps.search.tasks",
"apps.statistics.tasks",)
CELERYD_CONCURRENCY = 4
CELERY_IGNORE_RESULT = True
CELERY_ACKS_LATE = True # Retry if task fails
CELERYD_MAX_TASKS_PER_CHILD = 10
CELERYD_TASK_TIME_LIMIT = 12 * 30
CELERY_DISABLE_RATE_LIMITS = True
CELERY_WORKER_CONCURRENCY = 4
CELERY_TASK_IGNORE_RESULT = True
CELERY_TASK_ACKS_LATE = True # Retry if task fails
CELERY_WORKER_MAX_TASKS_PER_CHILD = 10
CELERY_TASK_TIME_LIMIT = 12 * 30
CELERY_WORKER_DISABLE_RATE_LIMITS = True
SECONDS_TO_DELAY_CELERY_EMAILS = 60
CELERYBEAT_SCHEDULE = {
CELERY_BEAT_SCHEDULE = {
'task-feeds': {
'task': 'task-feeds',
'schedule': datetime.timedelta(minutes=1),
@ -566,13 +559,8 @@ SESSION_REDIS_DB = 5
# = Elasticsearch =
# =================
if os.getenv("DOCKERBUILD"):
ELASTICSEARCH_FEED_HOSTS = ['db_search_feed:9200']
ELASTICSEARCH_STORY_HOSTS = ['db_search_story:9200']
else:
ELASTICSEARCH_FEED_HOSTS = ['127.0.0.1:9200']
ELASTICSEARCH_STORY_HOSTS = ['127.0.0.1:9200']
ELASTICSEARCH_FEED_HOSTS = ['db_search_feed:9200']
ELASTICSEARCH_STORY_HOSTS = ['db_search_story:9200']
# ===============
# = Social APIs =
@ -635,53 +623,50 @@ DEBUG_TOOLBAR_CONFIG = {
}
if DEBUG:
template_loaders = (
template_loaders = [
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
)
]
else:
template_loaders = (
template_loaders = [
('django.template.loaders.cached.Loader', (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
)),
)
]
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates'),
os.path.join(BASE_DIR, 'vendor/zebra/templates')],
'DIRS': [os.path.join(NEWSBLUR_DIR, 'templates'),
os.path.join(NEWSBLUR_DIR, 'vendor/zebra/templates')],
# 'APP_DIRS': True,
'OPTIONS': {
"context_processors": (
'context_processors': [
"django.contrib.auth.context_processors.auth",
"django.template.context_processors.debug",
"django.template.context_processors.media",
'django.template.context_processors.request',
),
"loaders": template_loaders,
"debug": DEBUG
],
'loaders': template_loaders,
},
}
]
# =========
# = Mongo =
# =========
if os.getenv("DOCKERBUILD"):
MONGO_DB_DEFAULTS = {
'name': 'newsblur',
'host': '127.0.0.1:27017',
'alias': 'default',
}
else:
MONGO_DB_DEFAULTS = {
'name': 'newsblur',
'host': 'db_mongo:27017',
'alias': 'default',
}
MONGO_DB_DEFAULTS = {
'name': 'newsblur',
'host': 'db_mongo:27017',
'alias': 'default',
'connect': False,
}
MONGO_DB = dict(MONGO_DB_DEFAULTS, **MONGO_DB)
MONGO_DB_NAME = MONGO_DB.pop('name')
# MONGO_URI = 'mongodb://%s' % (MONGO_DB.pop('host'),)
# if MONGO_DB.get('read_preference', pymongo.ReadPreference.PRIMARY) != pymongo.ReadPreference.PRIMARY:
@ -691,34 +676,26 @@ MONGO_DB = dict(MONGO_DB_DEFAULTS, **MONGO_DB)
# else:
# MONGOPRIMARYDB = MONGODB
# MONGODB = connect(MONGO_DB.pop('name'), host=MONGO_URI, **MONGO_DB)
MONGODB = connect(MONGO_DB.pop('name'), **MONGO_DB)
MONGODB = connect(MONGO_DB_NAME, **MONGO_DB)
# MONGODB = connect(host="mongodb://localhost:27017/newsblur", connect=False)
if os.getenv("DOCKERBUILD"):
MONGO_ANALYTICS_DB_DEFAULTS = {
'name': 'nbanalytics',
'host': 'db_mongo_analytics:27017',
'alias': 'nbanalytics',
}
else:
MONGO_ANALYTICS_DB_DEFAULTS = {
'name': 'nbanalytics',
'host': '127.0.0.1:27017',
'alias': 'nbanalytics',
}
MONGO_ANALYTICS_DB_DEFAULTS = {
'name': 'nbanalytics',
'host': 'db_mongo_analytics:27017',
'alias': 'nbanalytics',
}
MONGO_ANALYTICS_DB = dict(MONGO_ANALYTICS_DB_DEFAULTS, **MONGO_ANALYTICS_DB)
MONGO_ANALYTICS_DB_NAME = MONGO_ANALYTICS_DB.pop('name')
# MONGO_ANALYTICS_URI = 'mongodb://%s' % (MONGO_ANALYTICS_DB.pop('host'),)
# MONGOANALYTICSDB = connect(MONGO_ANALYTICS_DB.pop('name'), host=MONGO_ANALYTICS_URI, **MONGO_ANALYTICS_DB)
MONGOANALYTICSDB = connect(MONGO_ANALYTICS_DB.pop('name'), **MONGO_ANALYTICS_DB)
MONGOANALYTICSDB = connect(MONGO_ANALYTICS_DB_NAME, **MONGO_ANALYTICS_DB)
# =========
# = Redis =
# =========
BROKER_BACKEND = "redis"
BROKER_URL = "redis://%s:6379/%s" % (REDIS['host'], CELERY_REDIS_DB_NUM)
CELERY_RESULT_BACKEND = BROKER_URL
CELERY_BROKER_URL = "redis://%s:6379/%s" % (REDIS['host'], CELERY_REDIS_DB_NUM)
CELERY_RESULT_BACKEND = CELERY_BROKER_URL
SESSION_REDIS = {
'host': REDIS_SESSIONS['host'],
@ -736,7 +713,8 @@ CACHES = {
'LOCATION': '%s:6379' % REDIS['host'],
'OPTIONS': {
'DB': 6,
'PARSER_CLASS': 'redis.connection.HiredisParser'
'PARSER_CLASS': 'redis.connection.HiredisParser',
'SERIALIZER_CLASS': 'redis_cache.serializers.PickleSerializer'
},
},
}
@ -765,11 +743,11 @@ CELERY_ACCEPT_CONTENT = ['pickle', 'json', 'msgpack', 'yaml']
# = Assets =
# ==========
JAMMIT = jammit.JammitAssets(NEWSBLUR_DIR)
JAMMIT = jammit.JammitAssets(ROOT_DIR)
if DEBUG:
MIDDLEWARE += ('utils.request_introspection_middleware.DumpRequestMiddleware',)
MIDDLEWARE += ('utils.exception_middleware.ConsoleExceptionMiddleware',)
MIDDLEWARE_CLASSES += ('utils.request_introspection_middleware.DumpRequestMiddleware',)
MIDDLEWARE_CLASSES += ('utils.exception_middleware.ConsoleExceptionMiddleware',)
# =======
# = AWS =

View file

@ -6,7 +6,7 @@ https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/
"""
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "newsblur.settings")
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

View file

@ -32,7 +32,6 @@
'debug' : {{ debug|yesno:"true,false" }}
};
NEWSBLUR.Flags = {
'start_import_from_google_reader': {{ start_import_from_google_reader|yesno:"true,false" }}
};
NEWSBLUR.Preferences = {
'unread_view' : 0,
@ -95,7 +94,8 @@
'story_share_facebook' : true,
'story_share_readitlater' : false,
'story_share_instapaper' : true,
'story_share_email' : true
'story_share_email' : true,
'theme' : 'auto'
};
NEWSBLUR.URLs = {
'upload-avatar' : "{% url "upload-avatar" %}",
@ -130,7 +130,7 @@
<div class="NB-body-inner">
<div class="NB-splash-info NB-splash-top">
<a href="{% url "index" %}"><img class="NB-splash-title" src="{{ MEDIA_URL }}/img/logo_newsblur_blur.png" /></a>
<a href="{% url "index" %}"><div class="NB-splash-title NB-splash-blurred-logo"></div></a>
</div>
{% block content %}{% endblock %}

View file

@ -24,7 +24,6 @@
'MEDIA_URL' : "{{ MEDIA_URL }}"
};
NEWSBLUR.Flags = {
'start_import_from_google_reader': {{ start_import_from_google_reader|yesno:"true,false" }}
};
NEWSBLUR.URLs = {
'domain' : "{% current_domain %}"

View file

@ -36,9 +36,12 @@
{% endif %}
{% endblock %}
{% block bodyclass %}NB-body-main{% endblock %}
{% block content %}
{% block bodyclass %}
NB-body-main
{% if preferences.theme == "dark" %}NB-dark{% endif %}
{% endblock %}
{% block content %}
<h1 class="NB-splash-heading">NewsBlur</h1>
<h2 class="NB-splash-heading">- The best stories from your friends and favorite blogs, all in one place.</h2>
@ -116,12 +119,12 @@
<div class="NB-module-search-container">
<div class="NB-module-search-input NB-module-search-sites">
<div class="NB-search-close"></div>
<label for="search_site"><img src="{{ MEDIA_URL }}img/reader/search_icon2.png"></label>
<label for="search_site"><img src="{{ MEDIA_URL }}img/reader/search_light.png"></label>
<input id="search_site" name="search_site" placeholder="Site name or address...">
</div>
<div class="NB-module-search-input NB-module-search-people">
<div class="NB-search-close"></div>
<label for="search_people"><img src="{{ MEDIA_URL }}img/reader/search_icon2.png"></label>
<label for="search_people"><img src="{{ MEDIA_URL }}img/reader/search_light.png"></label>
<input id="search_people" name="search_people" placeholder="Username or email...">
</div>
<div class="NB-module-search-results"></div>

View file

@ -308,7 +308,6 @@
<div class="NB-story-pane-container">
<iframe id="feed_iframe" class="NB-feed-iframe"></iframe>
<div class="NB-feed-story-view NB-view-hidden">
<div class="NB-feed-story-view-floater"></div>
<div class="NB-feed-stories-container">
<div class="NB-feed-story-view-header NB-feedbar"></div>
<ul class="NB-feed-stories"></ul>

View file

@ -12,6 +12,8 @@
<li class="NB-splash-link NB-first NB-splash-link-twitter"><a href="http://twitter.com/newsblur">@newsblur</a></li>
</ul>
<div class="NB-splash-link NB-splash-link-logo"><a href="{% url "index" %}"><img src="{{ MEDIA_URL }}/img/logo_newsblur_blur.png" style="height: 30px;" /></a></div>
<div class="NB-splash-link NB-splash-link-logo">
<a href="{% url "index" %}"><div class="NB-splash-blurred-logo"></div></a>
</div>
</div>

View file

@ -8,6 +8,23 @@
<div class="NB-module-item">
<div class="NB-module-stats-counts">
<ul class="NB-menu-manage NB-menu-manage-notop">
<li class="NB-menu-item NB-menu-manage-theme">
<div class="NB-menu-manage-image"></div>
<ul class="segmented-control NB-options-theme">
<li class="NB-taskbar-button NB-theme-option NB-options-theme-light">
<div class="NB-task-image"></div>
<span class="NB-task-title">Light</span>
</li>
<li class="NB-taskbar-button NB-theme-option NB-options-theme-dark">
<div class="NB-task-image"></div>
<span class="NB-task-title">Dark</span>
</li>
<li class="NB-taskbar-button NB-theme-option NB-options-theme-auto">
<div class="NB-task-image"></div>
<span class="NB-task-title">Auto</span>
</li>
</ul>
</li>
<li class="NB-menu-item NB-menu-manage-preferences">
<div class="NB-menu-manage-image"></div>
<div class="NB-menu-manage-title">

View file

@ -58,10 +58,14 @@
</div>
</div>
</div>
<div class="NB-welcome-header-image {% if not post_request %}NB-active{% endif %}">
<img src="{{ MEDIA_URL }}img/welcome/header-web.png" class="NB-1">
<img src="{{ MEDIA_URL }}img/welcome/header-ios.png" class="NB-2">
<img src="{{ MEDIA_URL }}img/welcome/header-android.png" class="NB-3">
<div class="NB-welcome-header-image NB-1 {% if not post_request %}NB-active{% endif %}">
<img src="{{ MEDIA_URL }}img/welcome/header-web.png">
</div>
<div class="NB-welcome-header-image NB-2">
<img src="{{ MEDIA_URL }}img/welcome/header-ios.png">
</div>
<div class="NB-welcome-header-image NB-3">
<img src="{{ MEDIA_URL }}img/welcome/header-android.png">
</div>
<div class="NB-welcome-header-account {% if post_request %}NB-active{% endif %}">
@ -140,22 +144,22 @@
<div class="NB-feature">
<img src="{{ MEDIA_URL }}img/welcome/feature_1.png">
<div class="NB-feature-caption">Real-time RSS</div>
<div class="NB-feature-text">Stories are pushed directly to you, so you can read news as it comes in.</div>
<div class="NB-feature-text">Stories are pushed directly to you, so you can read news as it comes in</div>
</div>
<div class="NB-feature">
<img src="{{ MEDIA_URL }}img/welcome/feature_2.png">
<div class="NB-feature-caption">Original site</div>
<div class="NB-feature-text">Read the content in context, the way it was meant to be seen.</div>
<div class="NB-feature-caption">Original Site</div>
<div class="NB-feature-text">Read the content in context, the way it was meant to be seen</div>
</div>
<div class="NB-feature">
<img src="{{ MEDIA_URL }}img/welcome/feature_3.png">
<div class="NB-feature-caption">Shared stories</div>
<div class="NB-feature-text">Reading news is better with friends. Share stories on your public blurblog.</div>
<div class="NB-feature-caption">Shared Stories</div>
<div class="NB-feature-text">Reading news is better with friends. Share stories on your public blurblog</div>
</div>
<div class="NB-feature">
<img src="{{ MEDIA_URL }}img/welcome/feature_4.png">
<div class="NB-feature-caption">Training</div>
<div class="NB-feature-text">Hide the stories you don't like and highlight the stories you do.</div>
<div class="NB-feature-text">Hide the stories you don't like and highlight the stories you do</div>
</div>
</div>
</div>
@ -165,24 +169,80 @@
<div class="NB-feature">
<img src="{{ MEDIA_URL }}img/welcome/feature_5.jpg">
<div class="NB-feature-caption">Full Text Search</div>
<div class="NB-feature-text">Quickly find stories across all of your subscriptions.</div>
<div class="NB-feature-text">Quickly find stories across all of your subscriptions</div>
</div>
<div class="NB-feature">
<img src="{{ MEDIA_URL }}img/welcome/feature_6.png">
<div class="NB-feature-caption">Story Tagging</div>
<div class="NB-feature-text">Save stories with custom tags for fast references.</div>
<div class="NB-feature-text">Save stories with custom tags for fast references</div>
</div>
<div class="NB-feature">
<img src="{{ MEDIA_URL }}img/welcome/feature_7.jpg">
<div class="NB-feature-caption">Blurblog Privacy</div>
<div class="NB-feature-text">Share stories with the world or only with your friends.</div>
<div class="NB-feature-text">Share stories with the world or only with your friends</div>
</div>
<div class="NB-feature">
<img src="{{ MEDIA_URL }}img/welcome/feature_8.jpg">
<div class="NB-feature-caption">Third-party Apps</div>
<div class="NB-feature-text">Supports <a href="http://reederapp.com" class="NB-splash-link" target="_blank">Reeder</a>, <a href="http://readkitapp.com" class="NB-splash-link" target="_blank">ReadKit</a>, <a href="http://supertop.co/unread/" class="NB-splash-link" target="_blank">Unread</a>, and loads more.</div>
<div class="NB-feature-text">Supports <a href="http://reederapp.com" class="NB-splash-link" target="_blank">Reeder</a>, <a href="http://readkitapp.com" class="NB-splash-link" target="_blank">ReadKit</a>, <a href="http://supertop.co/unread/" class="NB-splash-link" target="_blank">Unread</a>, and loads more</div>
</div>
</div>
</div>
</div>
<div class="NB-welcome-subfeatures">
<div class="NB-inner">
<div class="NB-feature">
<img src="{{ MEDIA_URL }}img/welcome/subfeature_1.png">
<div class="NB-feature-caption">Saved Searches</div>
<div class="NB-feature-text">Regularly used searches are conveniently given their own feeds</div>
</div>
<div class="NB-feature">
<img src="{{ MEDIA_URL }}img/welcome/header-ios.png">
<div class="NB-feature-caption">First-class iOS App</div>
<div class="NB-feature-text">The NewsBlur iOS app is free and is jam-packed with features</div>
</div>
<div class="NB-feature">
<img src="{{ MEDIA_URL }}img/welcome/subfeature_3.png">
<div class="NB-feature-caption">Read the Full Story</div>
<div class="NB-feature-text">The original story from truncated RSS feeds is seamlessly expanded</div>
</div>
<div class="NB-feature">
<img src="{{ MEDIA_URL }}img/welcome/header-android.png">
<div class="NB-feature-caption">First-class Android App</div>
<div class="NB-feature-text">The NewsBlur Android app is free and has it all</div>
</div>
<div class="NB-feature">
<img src="{{ MEDIA_URL }}img/welcome/subfeature_5.png">
<div class="NB-feature-caption">Track Changes</div>
<div class="NB-feature-text">See how a story evolved since it was first published</div>
</div>
<div class="NB-feature">
<img src="{{ MEDIA_URL }}img/welcome/subfeature_6.png">
<div class="NB-feature-caption">IFTTT Support</div>
<div class="NB-feature-text">Hook NewsBlur up to nearly every service on the web</div>
</div>
<div class="NB-feature">
<img src="{{ MEDIA_URL }}img/welcome/subfeature_7.png">
<div class="NB-feature-caption">Email Newsletters</div>
<div class="NB-feature-text">Read your email newsletters where they belong, in a news reader</div>
</div>
<div class="NB-feature">
<img src="{{ MEDIA_URL }}img/welcome/subfeature_8.png">
<div class="NB-feature-caption">Grid, List, Split</div>
<div class="NB-feature-text">Every site has its own story layout, so you can mix and match</div>
</div>
<div class="NB-feature">
<img src="{{ MEDIA_URL }}img/welcome/subfeature_9.png">
<div class="NB-feature-caption">Dark Mode</div>
<div class="NB-feature-text">Easy on the eyes and built into the web, iOS, and Android</div>
</div>
<div class="NB-feature">
<img src="{{ MEDIA_URL }}img/welcome/subfeature_10.png">
<div class="NB-feature-caption">Twitter & YouTube</div>
<div class="NB-feature-text">Even sites that don't publish RSS feeds can be followed</div>
</div>
</div>
</div>
<div class="NB-welcome-pricing">

View file

@ -1,6 +1,6 @@
import sys
from mongoengine.queryset import OperationError
from mongoengine.base import ValidationError
from mongoengine.errors import ValidationError
from apps.analyzer.models import MClassifierFeed
from apps.analyzer.models import MClassifierAuthor
from apps.analyzer.models import MClassifierTag

View file

@ -28,6 +28,7 @@ from utils import log as logging
from utils.feed_functions import timelimit, TimeoutError
from qurl import qurl
from BeautifulSoup import BeautifulSoup
from mongoengine import connect, connection
from django.utils import feedgenerator
from django.utils.html import linebreaks
from django.utils.encoding import smart_unicode
@ -237,7 +238,7 @@ class FetchFeed:
return
if channel_id:
video_ids_xml = requests.get("https://www.youtube.com/feeds/videos.xml?channel_id=%s" % channel_id, verify=False)
video_ids_xml = requests.get("https://www.youtube.com/feeds/videos.xml?channel_id=%s" % channel_id)
channel_json = requests.get("https://www.googleapis.com/youtube/v3/channels?part=snippet&id=%s&key=%s" %
(channel_id, settings.YOUTUBE_API_KEY))
channel = json.decode(channel_json.content)
@ -257,7 +258,7 @@ class FetchFeed:
return
channel_url = "https://www.youtube.com/playlist?list=%s" % list_id
elif username:
video_ids_xml = requests.get("https://www.youtube.com/feeds/videos.xml?user=%s" % username, verify=False)
video_ids_xml = requests.get("https://www.youtube.com/feeds/videos.xml?user=%s" % username)
description = "YouTube videos uploaded by %s" % username
else:
return
@ -641,6 +642,12 @@ class Dispatcher:
return Feed.get_by_id(feed_id)
def process_feed_wrapper(self, feed_queue):
connection._connections = {}
connection._connection_settings ={}
connection._dbs = {}
settings.MONGODB = connect(settings.MONGO_DB_NAME, **settings.MONGO_DB)
settings.MONGOANALYTICSDB = connect(settings.MONGO_ANALYTICS_DB_NAME, **settings.MONGO_ANALYTICS_DB)
delta = None
current_process = multiprocessing.current_process()
identity = "X"

View file

@ -9,6 +9,7 @@ from django.conf import settings
from django.http import HttpResponse, HttpResponseForbidden, Http404
from django.core.mail import mail_admins
from django.db.models.query import QuerySet
from django.utils.deprecation import CallableBool
from mongoengine.queryset.queryset import QuerySet as MongoQuerySet
from bson.objectid import ObjectId
import sys
@ -50,6 +51,8 @@ def json_encode(data, *args, **kwargs):
# Same as for lists above.
elif isinstance(data, dict):
ret = _dict(data)
elif isinstance(data, CallableBool):
ret = bool(data)
elif isinstance(data, (Decimal, ObjectId)):
# json.dumps() cant handle Decimal
ret = str(data)

Some files were not shown because too many files have changed in this diff Show more