diff --git a/apps/categories/models.py b/apps/categories/models.py index d55717f29..954cda35e 100644 --- a/apps/categories/models.py +++ b/apps/categories/models.py @@ -56,7 +56,11 @@ class MCategory(mongo.Document): category_groups = groupby(sorted(category_sites, key=lambda c: c.category_title), key=lambda c: c.category_title) for category_title, sites in category_groups: - category = cls.objects.get(title=category_title) + try: + category = cls.objects.get(title=category_title) + except cls.DoesNotExist, e: + print " ***> Missing category: %s" % category_title + continue category.feed_ids = [site.feed_id for site in sites] category.save() print " ---> Reloaded category: %s" % category diff --git a/apps/profile/models.py b/apps/profile/models.py index 6cfd92dd2..2ca4488de 100644 --- a/apps/profile/models.py +++ b/apps/profile/models.py @@ -20,13 +20,14 @@ from apps.reader.models import UserSubscription from apps.rss_feeds.models import Feed, MStory from apps.rss_feeds.tasks import NewFeeds from apps.rss_feeds.tasks import SchedulePremiumSetup -from apps.feed_import.models import GoogleReaderImporter +from apps.feed_import.models import GoogleReaderImporter, OPMLExporter from utils import log as logging from utils import json_functions as json from utils.user_functions import generate_secret_token from vendor.timezones.fields import TimeZoneField from vendor.paypal.standard.ipn.signals import subscription_signup, payment_was_successful from vendor.paypal.standard.ipn.models import PayPalIPN +from vendor.paypalapi.interface import PayPalInterface from zebra.signals import zebra_webhook_customer_subscription_created from zebra.signals import zebra_webhook_charge_succeeded @@ -246,18 +247,51 @@ class Profile(models.Model): stripe_customer = stripe.Customer.retrieve(self.stripe_id) stripe_payments = stripe.Charge.all(customer=stripe_customer.id).data stripe_payments[0].refund() - logging.user(self.user, "~FRRefunding stripe payment: $%s" % (stripe_payments[0].amount/100)) - self.cancel_premium() refunded = stripe_payments[0].amount/100 + logging.user(self.user, "~FRRefunding stripe payment: $%s" % refunded) + self.cancel_premium() + else: + paypal_opts = { + 'API_ENVIRONMENT': 'PRODUCTION', + 'API_USERNAME': settings.PAYPAL_API_USERNAME, + 'API_PASSWORD': settings.PAYPAL_API_PASSWORD, + 'API_SIGNATURE': settings.PAYPAL_API_SIGNATURE, + } + paypal = PayPalInterface(**paypal_opts) + transaction = PayPalIPN.objects.filter(custom=self.user.username, + txn_type='subscr_payment')[0] + refund = paypal.refund_transaction(transaction.txn_id) + refunded = int(float(refund['raw']['TOTALREFUNDEDAMOUNT'][0])) + logging.user(self.user, "~FRRefunding paypal payment: $%s" % refunded) + self.cancel_premium() return refunded def cancel_premium(self): - self.cancel_premium_paypal() - return self.cancel_premium_stripe() + paypal_cancel = self.cancel_premium_paypal() + stripe_cancel = self.cancel_premium_stripe() + return paypal_cancel or stripe_cancel def cancel_premium_paypal(self): - pass + transactions = PayPalIPN.objects.filter(custom=self.user.username, + txn_type='subscr_signup') + if not transactions: + return + + paypal_opts = { + 'API_ENVIRONMENT': 'PRODUCTION', + 'API_USERNAME': settings.PAYPAL_API_USERNAME, + 'API_PASSWORD': settings.PAYPAL_API_PASSWORD, + 'API_SIGNATURE': settings.PAYPAL_API_SIGNATURE, + } + paypal = PayPalInterface(**paypal_opts) + transaction = transactions[0] + profileid = transaction.subscr_id + paypal.manage_recurring_payments_profile_status(profileid=profileid, action='Cancel') + + logging.user(self.user, "~FRCanceling Paypal subscription") + + return True def cancel_premium_stripe(self): if not self.stripe_id: @@ -270,7 +304,7 @@ class Profile(models.Model): logging.user(self.user, "~FRCanceling Stripe subscription") return True - + def queue_new_feeds(self, new_feeds=None): if not new_feeds: new_feeds = UserSubscription.objects.filter(user=self.user, @@ -319,7 +353,34 @@ class Profile(models.Model): msg.send(fail_silently=True) logging.user(self.user, "~BB~FM~SBSending email for new user: %s" % self.user.email) + + def send_opml_export_email(self): + if not self.user.email: + return + + MSentEmail.objects.get_or_create(receiver_user_id=self.user.pk, + email_type='opml_export') + + exporter = OPMLExporter(self.user) + opml = exporter.process() + params = { + 'feed_count': UserSubscription.objects.filter(user=self.user).count(), + } + user = self.user + text = render_to_string('mail/email_opml_export.txt', params) + html = render_to_string('mail/email_opml_export.xhtml', params) + subject = "Backup OPML file of your NewsBlur sites" + filename= 'NewsBlur Subscriptions - %s.xml' % datetime.datetime.now().strftime('%Y-%m-%d') + msg = EmailMultiAlternatives(subject, text, + from_email='NewsBlur <%s>' % settings.HELLO_EMAIL, + to=['%s <%s>' % (user, user.email)]) + msg.attach_alternative(html, "text/html") + msg.attach(filename, opml, 'text/xml') + msg.send(fail_silently=True) + + logging.user(self.user, "~BB~FM~SBSending OPML backup email to: %s" % self.user.email) + def send_first_share_to_blurblog_email(self, force=False): from apps.social.models import MSocialProfile, MSharedStory diff --git a/apps/profile/urls.py b/apps/profile/urls.py index f89f06c1d..ac8916935 100644 --- a/apps/profile/urls.py +++ b/apps/profile/urls.py @@ -21,4 +21,5 @@ urlpatterns = patterns('', url(r'^delete_account/?', views.delete_account, name='profile-delete-account'), url(r'^forgot_password_return/?', views.forgot_password_return, name='profile-forgot-password-return'), url(r'^forgot_password/?', views.forgot_password, name='profile-forgot-password'), + url(r'^delete_all_sites/?', views.delete_all_sites, name='profile-delete-all-sites'), ) diff --git a/apps/profile/views.py b/apps/profile/views.py index 78569efcb..d9df2785c 100644 --- a/apps/profile/views.py +++ b/apps/profile/views.py @@ -13,7 +13,7 @@ from django.shortcuts import render_to_response from django.core.mail import mail_admins from django.conf import settings from apps.profile.models import Profile, PaymentHistory, RNewUserQueue -from apps.reader.models import UserSubscription +from apps.reader.models import UserSubscription, UserSubscriptionFolders from apps.profile.forms import StripePlusPaymentForm, PLANS, DeleteAccountForm from apps.profile.forms import ForgotPasswordForm, ForgotPasswordReturnForm, AccountSettingsForm from apps.social.models import MSocialServices, MActivity, MSocialProfile @@ -217,6 +217,7 @@ def stripe_form(request): stripe.api_key = settings.STRIPE_SECRET plan = int(request.GET.get('plan', 2)) plan = PLANS[plan-1][0] + error = None if request.method == 'POST': zebra_form = StripePlusPaymentForm(request.POST, email=user.email) @@ -224,19 +225,22 @@ def stripe_form(request): user.email = zebra_form.cleaned_data['email'] user.save() - customer = stripe.Customer.create(**{ - 'card': zebra_form.cleaned_data['stripe_token'], - 'plan': zebra_form.cleaned_data['plan'], - 'email': user.email, - 'description': user.username, - }) - - user.profile.strip_4_digits = zebra_form.cleaned_data['last_4_digits'] - user.profile.stripe_id = customer.id - user.profile.save() - user.profile.activate_premium() # TODO: Remove, because webhooks are slow + try: + customer = stripe.Customer.create(**{ + 'card': zebra_form.cleaned_data['stripe_token'], + 'plan': zebra_form.cleaned_data['plan'], + 'email': user.email, + 'description': user.username, + }) + except stripe.CardError: + error = "This card was declined." + else: + user.profile.strip_4_digits = zebra_form.cleaned_data['last_4_digits'] + user.profile.stripe_id = customer.id + user.profile.save() + user.profile.activate_premium() # TODO: Remove, because webhooks are slow - success_updating = True + success_updating = True else: zebra_form = StripePlusPaymentForm(email=user.email, plan=plan) @@ -262,6 +266,7 @@ def stripe_form(request): 'new_user_queue_count': new_user_queue_count - 1, 'new_user_queue_position': new_user_queue_position, 'new_user_queue_behind': new_user_queue_behind, + 'error': error, }, context_instance=RequestContext(request) ) @@ -389,4 +394,21 @@ def forgot_password_return(request): return { 'forgot_password_return_form': form, - } \ No newline at end of file + } + +@ajax_login_required +@json.json_view +def delete_all_sites(request): + request.user.profile.send_opml_export_email() + + subs = UserSubscription.objects.filter(user=request.user) + sub_count = subs.count() + subs.delete() + + usf = UserSubscriptionFolders.objects.get(user=request.user) + usf.folders = '[]' + usf.save() + + logging.user(request.user, "~BC~FRDeleting %s sites" % sub_count) + + return dict(code=1) \ No newline at end of file diff --git a/apps/recommendations/views.py b/apps/recommendations/views.py index cb4ae7c59..0bf55dbec 100644 --- a/apps/recommendations/views.py +++ b/apps/recommendations/views.py @@ -44,6 +44,7 @@ def load_recommended_feed(request): 'has_previous_page' : page != 0, 'unmoderated' : unmoderated, 'today' : datetime.datetime.now(), + 'page' : page, }, context_instance=RequestContext(request)) else: return HttpResponse("") diff --git a/apps/rss_feeds/tasks.py b/apps/rss_feeds/tasks.py index 7cf0c0235..2d2225322 100644 --- a/apps/rss_feeds/tasks.py +++ b/apps/rss_feeds/tasks.py @@ -52,7 +52,7 @@ class TaskFeeds(Task): cp2 = time.time() # Mistakenly inactive feeds - hours_ago = (now - datetime.timedelta(hours=1)).strftime('%s') + hours_ago = (now - datetime.timedelta(minutes=10)).strftime('%s') old_tasked_feeds = r.zrangebyscore('tasked_feeds', 0, hours_ago) inactive_count = len(old_tasked_feeds) if inactive_count: diff --git a/apps/social/models.py b/apps/social/models.py index 3ff48dcfc..75ec22354 100644 --- a/apps/social/models.py +++ b/apps/social/models.py @@ -2014,7 +2014,7 @@ class MSocialServices(mongo.Document): }, 'gravatar': { 'gravatar_picture_url': "https://www.gravatar.com/avatar/" + \ - hashlib.md5(user.email).hexdigest() + hashlib.md5(user.email.lower()).hexdigest() }, 'upload': { 'upload_picture_url': self.upload_picture_url diff --git a/config/postgresql.conf b/config/postgresql.conf index 283d3b6c4..17517f32f 100644 --- a/config/postgresql.conf +++ b/config/postgresql.conf @@ -61,7 +61,7 @@ listen_addresses = '*' # what IP address(es) to listen on; # defaults to 'localhost', '*' = all # (change requires restart) port = 5432 # (change requires restart) -max_connections = 200 # (change requires restart) +max_connections = 1000 # (change requires restart) # Note: Increasing max_connections costs ~400 bytes of shared memory per # connection slot, plus lock space (see max_locks_per_transaction). #superuser_reserved_connections = 3 # (change requires restart) @@ -115,8 +115,8 @@ shared_buffers = 512MB # min 128kB # per transaction slot, plus lock space (see max_locks_per_transaction). # It is not advisable to set max_prepared_transactions nonzero unless you # actively intend to use prepared transactions. -work_mem = 64MB # min 64kB -maintenance_work_mem = 2GB # min 1MB +work_mem = 5MB # min 64kB +maintenance_work_mem = 512MB # min 1MB #max_stack_depth = 2MB # min 100kB # - Kernel Resource Usage - @@ -411,7 +411,7 @@ log_line_prefix = '%t %h' # special values: # AUTOVACUUM PARAMETERS #------------------------------------------------------------------------------ -#autovacuum = on # Enable autovacuum subprocess? 'on' +autovacuum = on # Enable autovacuum subprocess? 'on' # requires track_counts to also be on. #log_autovacuum_min_duration = -1 # -1 disables, 0 logs all actions and # their durations, > 0 logs only diff --git a/fabfile.py b/fabfile.py index fa2b2da68..947e67cd5 100644 --- a/fabfile.py +++ b/fabfile.py @@ -1064,6 +1064,7 @@ def setup_do(name, size=2): env.host_string = host time.sleep(10) add_user_to_do() + do() def do_name(name): if re.search(r"[0-9]", name): diff --git a/media/android/NewsBlur/res/drawable-hdpi/save.png b/media/android/NewsBlur/res/drawable-hdpi/save.png deleted file mode 100644 index 306d55a9c..000000000 Binary files a/media/android/NewsBlur/res/drawable-hdpi/save.png and /dev/null differ diff --git a/media/android/NewsBlur/res/drawable-hdpi/tag.png b/media/android/NewsBlur/res/drawable-hdpi/tag.png deleted file mode 100644 index 432e7c00c..000000000 Binary files a/media/android/NewsBlur/res/drawable-hdpi/tag.png and /dev/null differ diff --git a/media/android/NewsBlur/res/drawable-hdpi/tag_negative.png b/media/android/NewsBlur/res/drawable-hdpi/tag_negative.png deleted file mode 100644 index b14ef2e54..000000000 Binary files a/media/android/NewsBlur/res/drawable-hdpi/tag_negative.png and /dev/null differ diff --git a/media/android/NewsBlur/res/drawable-hdpi/tag_positive.png b/media/android/NewsBlur/res/drawable-hdpi/tag_positive.png deleted file mode 100644 index a6fd7f325..000000000 Binary files a/media/android/NewsBlur/res/drawable-hdpi/tag_positive.png and /dev/null differ diff --git a/media/android/NewsBlur/res/drawable-hdpi/user_light.png b/media/android/NewsBlur/res/drawable-hdpi/user_light.png deleted file mode 100644 index 39c5bb390..000000000 Binary files a/media/android/NewsBlur/res/drawable-hdpi/user_light.png and /dev/null differ diff --git a/media/android/NewsBlur/res/drawable-ldpi/tag_negative.png b/media/android/NewsBlur/res/drawable-ldpi/tag_negative.png deleted file mode 100644 index 5d71744f7..000000000 Binary files a/media/android/NewsBlur/res/drawable-ldpi/tag_negative.png and /dev/null differ diff --git a/media/android/NewsBlur/res/drawable-ldpi/tag_positive.png b/media/android/NewsBlur/res/drawable-ldpi/tag_positive.png deleted file mode 100644 index 89a65731e..000000000 Binary files a/media/android/NewsBlur/res/drawable-ldpi/tag_positive.png and /dev/null differ diff --git a/media/android/NewsBlur/res/drawable-mdpi/save.png b/media/android/NewsBlur/res/drawable-mdpi/save.png deleted file mode 100644 index ad0769553..000000000 Binary files a/media/android/NewsBlur/res/drawable-mdpi/save.png and /dev/null differ diff --git a/media/android/NewsBlur/res/drawable-mdpi/tag.png b/media/android/NewsBlur/res/drawable-mdpi/tag.png deleted file mode 100644 index b85d7c580..000000000 Binary files a/media/android/NewsBlur/res/drawable-mdpi/tag.png and /dev/null differ diff --git a/media/android/NewsBlur/res/drawable-mdpi/tag_negative.png b/media/android/NewsBlur/res/drawable-mdpi/tag_negative.png deleted file mode 100644 index f330abf56..000000000 Binary files a/media/android/NewsBlur/res/drawable-mdpi/tag_negative.png and /dev/null differ diff --git a/media/android/NewsBlur/res/drawable-mdpi/tag_positive.png b/media/android/NewsBlur/res/drawable-mdpi/tag_positive.png deleted file mode 100644 index be6197342..000000000 Binary files a/media/android/NewsBlur/res/drawable-mdpi/tag_positive.png and /dev/null differ diff --git a/media/android/NewsBlur/res/drawable-xhdpi/save.png b/media/android/NewsBlur/res/drawable-xhdpi/save.png deleted file mode 100644 index 272060253..000000000 Binary files a/media/android/NewsBlur/res/drawable-xhdpi/save.png and /dev/null differ diff --git a/media/android/NewsBlur/res/drawable-xhdpi/tag.png b/media/android/NewsBlur/res/drawable-xhdpi/tag.png deleted file mode 100644 index 8fdcd1a23..000000000 Binary files a/media/android/NewsBlur/res/drawable-xhdpi/tag.png and /dev/null differ diff --git a/media/android/NewsBlur/res/drawable-xhdpi/user_light.png b/media/android/NewsBlur/res/drawable-xhdpi/user_light.png deleted file mode 100644 index 39c5bb390..000000000 Binary files a/media/android/NewsBlur/res/drawable-xhdpi/user_light.png and /dev/null differ diff --git a/media/android/NewsBlur/res/drawable/divider_dark.xml b/media/android/NewsBlur/res/drawable/divider_dark.xml deleted file mode 100644 index 0274f74b5..000000000 --- a/media/android/NewsBlur/res/drawable/divider_dark.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/media/android/NewsBlur/res/drawable/divider_folder.xml b/media/android/NewsBlur/res/drawable/divider_folder.xml deleted file mode 100644 index 50764277c..000000000 --- a/media/android/NewsBlur/res/drawable/divider_folder.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/media/android/NewsBlur/res/drawable/fleuron.png b/media/android/NewsBlur/res/drawable/fleuron.png deleted file mode 100644 index 6053ca2a3..000000000 Binary files a/media/android/NewsBlur/res/drawable/fleuron.png and /dev/null differ diff --git a/media/android/NewsBlur/res/drawable/gradient_activation_highlight.xml b/media/android/NewsBlur/res/drawable/gradient_activation_highlight.xml new file mode 100644 index 000000000..db71a6929 --- /dev/null +++ b/media/android/NewsBlur/res/drawable/gradient_activation_highlight.xml @@ -0,0 +1,8 @@ + + + + diff --git a/media/android/NewsBlur/res/drawable/list_background_pressed.xml b/media/android/NewsBlur/res/drawable/list_background_pressed.xml deleted file mode 100644 index 86bc00a5a..000000000 --- a/media/android/NewsBlur/res/drawable/list_background_pressed.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/media/android/NewsBlur/res/drawable/negative_count_rect.xml b/media/android/NewsBlur/res/drawable/negative_count_rect.xml deleted file mode 100644 index 36327f0d7..000000000 --- a/media/android/NewsBlur/res/drawable/negative_count_rect.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/media/android/NewsBlur/res/drawable/repeating_pattern.png b/media/android/NewsBlur/res/drawable/repeating_pattern.png deleted file mode 100644 index 3ea7a0973..000000000 Binary files a/media/android/NewsBlur/res/drawable/repeating_pattern.png and /dev/null differ diff --git a/media/android/NewsBlur/res/drawable/savebutton_background_default.xml b/media/android/NewsBlur/res/drawable/savebutton_background_default.xml deleted file mode 100644 index 8ee79837c..000000000 --- a/media/android/NewsBlur/res/drawable/savebutton_background_default.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/media/android/NewsBlur/res/drawable/savebutton_background_pressed.xml b/media/android/NewsBlur/res/drawable/savebutton_background_pressed.xml deleted file mode 100644 index 77d88ea91..000000000 --- a/media/android/NewsBlur/res/drawable/savebutton_background_pressed.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/media/android/NewsBlur/res/drawable/selector_feed_background.xml b/media/android/NewsBlur/res/drawable/selector_feed_background.xml index 34084444d..d5a2a0bb7 100644 --- a/media/android/NewsBlur/res/drawable/selector_feed_background.xml +++ b/media/android/NewsBlur/res/drawable/selector_feed_background.xml @@ -1,5 +1,5 @@ - + - \ No newline at end of file + diff --git a/media/android/NewsBlur/res/drawable/selector_folder_background.xml b/media/android/NewsBlur/res/drawable/selector_folder_background.xml index 41b96899a..7ce753d96 100644 --- a/media/android/NewsBlur/res/drawable/selector_folder_background.xml +++ b/media/android/NewsBlur/res/drawable/selector_folder_background.xml @@ -1,5 +1,5 @@ - + diff --git a/media/android/NewsBlur/res/drawable/sharebutton_background_default.xml b/media/android/NewsBlur/res/drawable/sharebutton_background_default.xml deleted file mode 100644 index 8ee79837c..000000000 --- a/media/android/NewsBlur/res/drawable/sharebutton_background_default.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/media/android/NewsBlur/res/drawable/sharebutton_background_pressed.xml b/media/android/NewsBlur/res/drawable/sharebutton_background_pressed.xml deleted file mode 100644 index 77d88ea91..000000000 --- a/media/android/NewsBlur/res/drawable/sharebutton_background_pressed.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/media/android/NewsBlur/res/drawable/shiny_plastic.xml b/media/android/NewsBlur/res/drawable/shiny_plastic.xml deleted file mode 100644 index 328c7be67..000000000 --- a/media/android/NewsBlur/res/drawable/shiny_plastic.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/media/android/NewsBlur/res/drawable/textfield_activated_blur.9.png b/media/android/NewsBlur/res/drawable/textfield_activated_blur.9.png deleted file mode 100644 index fdcdf0797..000000000 Binary files a/media/android/NewsBlur/res/drawable/textfield_activated_blur.9.png and /dev/null differ diff --git a/media/android/NewsBlur/res/drawable/textfield_default_blur.9.png b/media/android/NewsBlur/res/drawable/textfield_default_blur.9.png deleted file mode 100644 index 3d8898e90..000000000 Binary files a/media/android/NewsBlur/res/drawable/textfield_default_blur.9.png and /dev/null differ diff --git a/media/android/NewsBlur/res/drawable/textfield_disabled_blur.9.png b/media/android/NewsBlur/res/drawable/textfield_disabled_blur.9.png deleted file mode 100644 index 09b561642..000000000 Binary files a/media/android/NewsBlur/res/drawable/textfield_disabled_blur.9.png and /dev/null differ diff --git a/media/android/NewsBlur/res/drawable/textfield_disabled_focused_blur.9.png b/media/android/NewsBlur/res/drawable/textfield_disabled_focused_blur.9.png deleted file mode 100644 index 3b9289448..000000000 Binary files a/media/android/NewsBlur/res/drawable/textfield_disabled_focused_blur.9.png and /dev/null differ diff --git a/media/android/NewsBlur/res/drawable/textfield_focused_holo_light.9.png b/media/android/NewsBlur/res/drawable/textfield_focused_holo_light.9.png deleted file mode 100644 index efbaef8b4..000000000 Binary files a/media/android/NewsBlur/res/drawable/textfield_focused_holo_light.9.png and /dev/null differ diff --git a/media/android/NewsBlur/res/layout/include_reading_item_comment.xml b/media/android/NewsBlur/res/layout/include_reading_item_comment.xml index 2fb23a28e..6c4c63d4d 100644 --- a/media/android/NewsBlur/res/layout/include_reading_item_comment.xml +++ b/media/android/NewsBlur/res/layout/include_reading_item_comment.xml @@ -1,24 +1,33 @@ -