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.
...
23
UPGRADING.md
Normal 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
|
|
@ -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'),
|
||||
]
|
||||
|
|
|
@ -230,4 +230,3 @@ class UploadedOPML(mongo.Document):
|
|||
'order': '-upload_date',
|
||||
'indexes': ['user_id', '-upload_date'],
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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']
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)" % (
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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':
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 78 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 184 KiB |
BIN
media/img/logo_newsblur_blur_dark.png
Normal file
After Width: | Height: | Size: 200 KiB |
Before Width: | Height: | Size: 408 KiB After Width: | Height: | Size: 408 KiB |
BIN
media/img/reader/half.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
media/img/reader/mute_grey.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
media/img/reader/next_page.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
media/img/reader/next_page_active.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
media/img/reader/paintbrush.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
media/img/reader/palette.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
media/img/reader/previous_page.png
Normal file
After Width: | Height: | Size: 3 KiB |
BIN
media/img/reader/previous_page_active.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
1
media/img/reader/ring_spinner.svg
Normal 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
After Width: | Height: | Size: 4.3 KiB |
BIN
media/img/reader/search_light.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
18
media/img/reader/sun_loader_dark.svg
Normal 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 |
18
media/img/reader/sun_loader_light.svg
Normal 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
After Width: | Height: | Size: 2.4 KiB |
1
media/img/reader/worm_spinner.svg
Normal 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 |
186116
media/img/vectors/NewsBlur Horizontal Blur.ai
Normal file
4280
media/img/vectors/NewsBlur Horizontal.ai
Normal file
BIN
media/img/welcome/subfeature_1.png
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
media/img/welcome/subfeature_10.png
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
media/img/welcome/subfeature_3.png
Normal file
After Width: | Height: | Size: 81 KiB |
BIN
media/img/welcome/subfeature_5.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
media/img/welcome/subfeature_6.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
media/img/welcome/subfeature_6a.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
media/img/welcome/subfeature_7.png
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
media/img/welcome/subfeature_8.png
Normal file
After Width: | Height: | Size: 90 KiB |
BIN
media/img/welcome/subfeature_9.png
Normal file
After Width: | Height: | Size: 75 KiB |
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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..." })
|
||||
]),
|
||||
|
|
|
@ -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. ',
|
||||
|
|
|
@ -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});
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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' }, [
|
||||
|
|
|
@ -6,8 +6,6 @@ NEWSBLUR.utils = {
|
|||
return 'Twitter';
|
||||
case 'facebook':
|
||||
return 'Facebook';
|
||||
case 'appdotnet':
|
||||
return 'App.net';
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -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')),
|
||||
' · ',
|
||||
|
|
|
@ -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() %>\
|
||||
<% } %>\
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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' }, [
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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
|
@ -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
|
@ -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()
|
|
@ -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 =
|
|
@ -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()
|
|
@ -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 %}
|
||||
|
|
|
@ -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 %}"
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|