2010-06-30 13:36:51 -04:00
|
|
|
import datetime
|
2012-07-09 13:55:29 -07:00
|
|
|
import mongoengine as mongo
|
2009-06-16 03:08:55 +00:00
|
|
|
from django.db import models
|
2010-10-25 20:20:59 -04:00
|
|
|
from django.db import IntegrityError
|
2011-07-17 20:53:30 -07:00
|
|
|
from django.db.utils import DatabaseError
|
2010-05-11 21:36:17 -04:00
|
|
|
from django.db.models.signals import post_save
|
2011-09-19 08:56:16 -07:00
|
|
|
from django.conf import settings
|
2010-11-08 12:09:55 -05:00
|
|
|
from django.contrib.auth import authenticate
|
2011-09-19 08:56:16 -07:00
|
|
|
from django.contrib.auth.models import User
|
|
|
|
from django.core.mail import mail_admins
|
|
|
|
from django.core.mail import EmailMultiAlternatives
|
2011-09-19 09:46:36 -07:00
|
|
|
from django.core.urlresolvers import reverse
|
2011-09-19 08:56:16 -07:00
|
|
|
from django.template.loader import render_to_string
|
2010-10-16 23:24:55 -04:00
|
|
|
from apps.reader.models import UserSubscription
|
2010-10-29 09:02:54 -04:00
|
|
|
from apps.rss_feeds.models import Feed
|
2011-01-30 23:56:51 -05:00
|
|
|
from apps.rss_feeds.tasks import NewFeeds
|
2010-10-23 12:29:18 -04:00
|
|
|
from utils import log as logging
|
2012-07-13 18:42:44 -07:00
|
|
|
from utils import json_functions as json
|
2011-01-20 09:57:23 -05:00
|
|
|
from utils.user_functions import generate_secret_token
|
2011-09-19 08:56:16 -07:00
|
|
|
from vendor.timezones.fields import TimeZoneField
|
|
|
|
from vendor.paypal.standard.ipn.signals import subscription_signup
|
2012-02-28 17:37:01 -08:00
|
|
|
from zebra.signals import zebra_webhook_customer_subscription_created
|
2011-09-19 08:56:16 -07:00
|
|
|
|
2010-05-11 21:36:17 -04:00
|
|
|
class Profile(models.Model):
|
2011-05-12 18:15:15 -04:00
|
|
|
user = models.OneToOneField(User, unique=True, related_name="profile")
|
|
|
|
is_premium = models.BooleanField(default=False)
|
2011-09-21 17:49:26 -07:00
|
|
|
send_emails = models.BooleanField(default=True)
|
2011-05-12 18:15:15 -04:00
|
|
|
preferences = models.TextField(default="{}")
|
|
|
|
view_settings = models.TextField(default="{}")
|
2010-09-05 18:08:08 -07:00
|
|
|
collapsed_folders = models.TextField(default="[]")
|
2011-05-12 18:15:15 -04:00
|
|
|
feed_pane_size = models.IntegerField(default=240)
|
2012-03-20 11:15:40 -07:00
|
|
|
tutorial_finished = models.BooleanField(default=False)
|
2012-03-21 12:28:51 -07:00
|
|
|
hide_getting_started = models.NullBooleanField(default=False, null=True, blank=True)
|
|
|
|
has_setup_feeds = models.NullBooleanField(default=False, null=True, blank=True)
|
|
|
|
has_found_friends = models.NullBooleanField(default=False, null=True, blank=True)
|
|
|
|
has_trained_intelligence = models.NullBooleanField(default=False, null=True, blank=True)
|
2011-08-07 21:38:09 -07:00
|
|
|
hide_mobile = models.BooleanField(default=False)
|
2011-05-12 18:15:15 -04:00
|
|
|
last_seen_on = models.DateTimeField(default=datetime.datetime.now)
|
|
|
|
last_seen_ip = models.CharField(max_length=50, blank=True, null=True)
|
2012-04-21 18:20:49 -07:00
|
|
|
dashboard_date = models.DateTimeField(default=datetime.datetime.now)
|
2011-05-12 18:15:15 -04:00
|
|
|
timezone = TimeZoneField(default="America/New_York")
|
|
|
|
secret_token = models.CharField(max_length=12, blank=True, null=True)
|
2012-02-27 21:46:34 -08:00
|
|
|
stripe_4_digits = models.CharField(max_length=4, blank=True, null=True)
|
|
|
|
stripe_id = models.CharField(max_length=24, blank=True, null=True)
|
2010-05-11 21:36:17 -04:00
|
|
|
|
2010-10-23 11:20:54 -04:00
|
|
|
def __unicode__(self):
|
2011-09-19 08:56:16 -07:00
|
|
|
return "%s <%s> (Premium: %s)" % (self.user, self.user.email, self.is_premium)
|
2011-01-20 09:57:23 -05:00
|
|
|
|
2012-07-13 18:42:44 -07:00
|
|
|
def to_json(self):
|
|
|
|
return {
|
|
|
|
'is_premium': self.is_premium,
|
|
|
|
'preferences': json.decode(self.preferences),
|
|
|
|
'tutorial_finished': self.tutorial_finished,
|
|
|
|
'hide_getting_started': self.hide_getting_started,
|
|
|
|
'has_setup_feeds': self.has_setup_feeds,
|
|
|
|
'has_found_friends': self.has_found_friends,
|
|
|
|
'has_trained_intelligence': self.has_trained_intelligence,
|
|
|
|
'hide_mobile': self.hide_mobile,
|
|
|
|
'dashboard_date': self.dashboard_date
|
|
|
|
}
|
|
|
|
|
2011-01-20 09:57:23 -05:00
|
|
|
def save(self, *args, **kwargs):
|
|
|
|
if not self.secret_token:
|
|
|
|
self.secret_token = generate_secret_token(self.user.username, 12)
|
2011-07-17 20:53:30 -07:00
|
|
|
try:
|
|
|
|
super(Profile, self).save(*args, **kwargs)
|
|
|
|
except DatabaseError:
|
|
|
|
print " ---> Profile not saved. Table isn't there yet."
|
2011-01-20 09:57:23 -05:00
|
|
|
|
2012-07-28 18:33:07 -07:00
|
|
|
def delete_user(self, confirm=False):
|
|
|
|
if not confirm:
|
|
|
|
print " ---> You must pass confirm=True to delete this user."
|
|
|
|
return
|
|
|
|
|
|
|
|
from apps.social.models import MSocialProfile, MSharedStory, MSocialSubscription
|
|
|
|
from apps.social.models import MActivity, MInteraction
|
2012-07-29 23:53:02 -07:00
|
|
|
try:
|
|
|
|
social_profile = MSocialProfile.objects.get(user_id=self.user.pk)
|
|
|
|
print " ---> Unfollowing %s followings and %s followers" % (social_profile.following_count,
|
|
|
|
social_profile.follower_count)
|
|
|
|
for follow in social_profile.following_user_ids:
|
|
|
|
social_profile.unfollow_user(follow)
|
|
|
|
for follower in social_profile.follower_user_ids:
|
|
|
|
follower_profile = MSocialProfile.objects.get(user_id=follower)
|
|
|
|
follower_profile.unfollow_user(self.user.pk)
|
|
|
|
social_profile.delete()
|
|
|
|
except MSocialProfile.DoesNotExist:
|
|
|
|
print " ***> No social profile found. S'ok, moving on."
|
|
|
|
pass
|
2012-07-28 18:33:07 -07:00
|
|
|
|
|
|
|
shared_stories = MSharedStory.objects.filter(user_id=self.user.pk)
|
|
|
|
print " ---> Deleting %s shared stories" % shared_stories.count()
|
|
|
|
for story in shared_stories:
|
|
|
|
story.delete()
|
|
|
|
|
|
|
|
subscriptions = MSocialSubscription.objects.filter(subscription_user_id=self.user.pk)
|
|
|
|
print " ---> Deleting %s social subscriptions" % subscriptions.count()
|
|
|
|
subscriptions.delete()
|
|
|
|
|
|
|
|
interactions = MInteraction.objects.filter(user_id=self.user.pk)
|
|
|
|
print " ---> Deleting %s interactions for user."
|
|
|
|
interactions.delete()
|
|
|
|
|
|
|
|
interactions = MInteraction.objects.filter(with_user_id=self.user.pk)
|
|
|
|
print " ---> Deleting %s interactions with user."
|
|
|
|
interactions.delete()
|
|
|
|
|
|
|
|
activities = MActivity.objects.filter(user_id=self.user.pk)
|
|
|
|
print " ---> Deleting %s activities for user."
|
|
|
|
activities.delete()
|
|
|
|
|
|
|
|
activities = MActivity.objects.filter(with_user_id=self.user.pk)
|
|
|
|
print " ---> Deleting %s activities with user."
|
|
|
|
activities.delete()
|
|
|
|
|
|
|
|
print " ---> Deleting user: %s" % self.user
|
|
|
|
self.user.delete()
|
|
|
|
|
2010-10-23 11:20:54 -04:00
|
|
|
def activate_premium(self):
|
2012-07-05 22:20:49 -07:00
|
|
|
from apps.profile.tasks import EmailNewPremium
|
|
|
|
EmailNewPremium.delay(user_id=self.user.pk)
|
2011-10-26 20:09:28 -07:00
|
|
|
|
2010-10-23 11:20:54 -04:00
|
|
|
self.is_premium = True
|
|
|
|
self.save()
|
|
|
|
|
|
|
|
subs = UserSubscription.objects.filter(user=self.user)
|
|
|
|
for sub in subs:
|
|
|
|
sub.active = True
|
2010-10-25 20:20:59 -04:00
|
|
|
try:
|
|
|
|
sub.save()
|
|
|
|
sub.feed.setup_feed_for_premium_subscribers()
|
2010-10-29 09:02:54 -04:00
|
|
|
except IntegrityError, Feed.DoesNotExist:
|
2010-10-25 20:20:59 -04:00
|
|
|
pass
|
2010-10-23 11:20:54 -04:00
|
|
|
|
2011-01-30 23:56:51 -05:00
|
|
|
self.queue_new_feeds()
|
2010-10-30 00:27:52 -04:00
|
|
|
|
2011-02-23 13:46:47 -05:00
|
|
|
logging.user(self.user, "~BY~SK~FW~SBNEW PREMIUM ACCOUNT! WOOHOO!!! ~FR%s subscriptions~SN!" % (subs.count()))
|
2010-10-23 11:20:54 -04:00
|
|
|
|
2011-01-30 23:56:51 -05:00
|
|
|
def queue_new_feeds(self, new_feeds=None):
|
|
|
|
if not new_feeds:
|
|
|
|
new_feeds = UserSubscription.objects.filter(user=self.user,
|
|
|
|
feed__fetched_once=False,
|
|
|
|
active=True).values('feed_id')
|
|
|
|
new_feeds = list(set([f['feed_id'] for f in new_feeds]))
|
2011-02-23 13:46:47 -05:00
|
|
|
logging.user(self.user, "~BB~FW~SBQueueing NewFeeds: ~FC(%s) %s" % (len(new_feeds), new_feeds))
|
2011-01-30 23:56:51 -05:00
|
|
|
size = 4
|
|
|
|
for t in (new_feeds[pos:pos + size] for pos in xrange(0, len(new_feeds), size)):
|
2012-07-12 23:58:29 -07:00
|
|
|
NewFeeds.apply_async(args=(t,), queue="new_feeds")
|
2011-01-30 23:56:51 -05:00
|
|
|
|
|
|
|
def refresh_stale_feeds(self, exclude_new=False):
|
2011-01-17 14:20:36 -05:00
|
|
|
stale_cutoff = datetime.datetime.now() - datetime.timedelta(days=7)
|
|
|
|
stale_feeds = UserSubscription.objects.filter(user=self.user, active=True, feed__last_update__lte=stale_cutoff)
|
2011-01-30 23:56:51 -05:00
|
|
|
if exclude_new:
|
|
|
|
stale_feeds = stale_feeds.filter(feed__fetched_once=True)
|
2011-01-17 14:20:36 -05:00
|
|
|
all_feeds = UserSubscription.objects.filter(user=self.user, active=True)
|
|
|
|
|
2011-02-23 13:46:47 -05:00
|
|
|
logging.user(self.user, "~FG~BBRefreshing stale feeds: ~SB%s/%s" % (
|
|
|
|
stale_feeds.count(), all_feeds.count()))
|
2011-01-17 14:20:36 -05:00
|
|
|
|
|
|
|
for sub in stale_feeds:
|
|
|
|
sub.feed.fetched_once = False
|
|
|
|
sub.feed.save()
|
|
|
|
|
2011-01-18 08:45:35 -05:00
|
|
|
if stale_feeds:
|
2012-01-26 09:32:24 -08:00
|
|
|
stale_feeds = list(set([f.feed_id for f in stale_feeds]))
|
2011-01-30 23:56:51 -05:00
|
|
|
self.queue_new_feeds(new_feeds=stale_feeds)
|
2011-09-19 08:56:16 -07:00
|
|
|
|
2011-09-21 17:49:26 -07:00
|
|
|
def send_new_user_email(self):
|
|
|
|
if not self.user.email or not self.send_emails:
|
2011-09-19 08:56:16 -07:00
|
|
|
return
|
|
|
|
|
|
|
|
user = self.user
|
|
|
|
text = render_to_string('mail/email_new_account.txt', locals())
|
|
|
|
html = render_to_string('mail/email_new_account.xhtml', locals())
|
|
|
|
subject = "Welcome to NewsBlur, %s" % (self.user.username)
|
|
|
|
msg = EmailMultiAlternatives(subject, text,
|
|
|
|
from_email='NewsBlur <%s>' % settings.HELLO_EMAIL,
|
|
|
|
to=['%s <%s>' % (user, user.email)])
|
|
|
|
msg.attach_alternative(html, "text/html")
|
2011-10-28 10:29:11 -07:00
|
|
|
msg.send(fail_silently=True)
|
2011-09-23 18:13:16 -07:00
|
|
|
|
|
|
|
logging.user(self.user, "~BB~FM~SBSending email for new user: %s" % self.user.email)
|
2011-09-19 08:56:16 -07:00
|
|
|
|
2011-10-26 20:09:28 -07:00
|
|
|
def send_new_premium_email(self, force=False):
|
2012-07-05 22:20:49 -07:00
|
|
|
subs = UserSubscription.objects.filter(user=self.user)
|
|
|
|
message = """Woohoo!
|
|
|
|
|
|
|
|
User: %(user)s
|
|
|
|
Feeds: %(feeds)s
|
|
|
|
|
|
|
|
Sincerely,
|
|
|
|
NewsBlur""" % {'user': self.user.username, 'feeds': subs.count()}
|
|
|
|
mail_admins('New premium account', message, fail_silently=True)
|
|
|
|
|
2011-09-21 17:49:26 -07:00
|
|
|
if not self.user.email or not self.send_emails:
|
2011-09-19 08:56:16 -07:00
|
|
|
return
|
|
|
|
|
2011-10-26 20:09:28 -07:00
|
|
|
if self.is_premium and not force:
|
|
|
|
return
|
|
|
|
|
2011-09-19 08:56:16 -07:00
|
|
|
user = self.user
|
|
|
|
text = render_to_string('mail/email_new_premium.txt', locals())
|
|
|
|
html = render_to_string('mail/email_new_premium.xhtml', locals())
|
|
|
|
subject = "Thanks for going premium on NewsBlur!"
|
|
|
|
msg = EmailMultiAlternatives(subject, text,
|
|
|
|
from_email='NewsBlur <%s>' % settings.HELLO_EMAIL,
|
|
|
|
to=['%s <%s>' % (user, user.email)])
|
|
|
|
msg.attach_alternative(html, "text/html")
|
2011-10-28 10:29:11 -07:00
|
|
|
msg.send(fail_silently=True)
|
2011-09-23 18:13:16 -07:00
|
|
|
|
|
|
|
logging.user(self.user, "~BB~FM~SBSending email for new premium: %s" % self.user.email)
|
2011-09-22 09:23:42 -07:00
|
|
|
|
|
|
|
def send_forgot_password_email(self, email=None):
|
|
|
|
if not self.user.email and not email:
|
|
|
|
print "Please provide an email address."
|
|
|
|
return
|
|
|
|
|
|
|
|
if not self.user.email and email:
|
|
|
|
self.user.email = email
|
|
|
|
self.user.save()
|
|
|
|
|
|
|
|
user = self.user
|
|
|
|
text = render_to_string('mail/email_forgot_password.txt', locals())
|
|
|
|
html = render_to_string('mail/email_forgot_password.xhtml', locals())
|
|
|
|
subject = "Forgot your password on NewsBlur?"
|
|
|
|
msg = EmailMultiAlternatives(subject, text,
|
|
|
|
from_email='NewsBlur <%s>' % settings.HELLO_EMAIL,
|
|
|
|
to=['%s <%s>' % (user, user.email)])
|
|
|
|
msg.attach_alternative(html, "text/html")
|
2011-10-28 10:29:11 -07:00
|
|
|
msg.send(fail_silently=True)
|
2011-09-22 09:23:42 -07:00
|
|
|
|
2012-03-11 13:26:57 -07:00
|
|
|
user.set_password('')
|
|
|
|
user.save()
|
|
|
|
|
2011-09-23 18:13:16 -07:00
|
|
|
logging.user(self.user, "~BB~FM~SBSending email for forgotten password: %s" % self.user.email)
|
|
|
|
|
2012-07-20 19:43:28 -07:00
|
|
|
def send_upload_opml_finished_email(self, feed_count):
|
|
|
|
if not self.user.email:
|
|
|
|
print "Please provide an email address."
|
|
|
|
return
|
|
|
|
|
|
|
|
user = self.user
|
|
|
|
text = render_to_string('mail/email_upload_opml_finished.txt', locals())
|
|
|
|
html = render_to_string('mail/email_upload_opml_finished.xhtml', locals())
|
|
|
|
subject = "Your OPML upload is complete. Get going with NewsBlur!"
|
|
|
|
msg = EmailMultiAlternatives(subject, text,
|
|
|
|
from_email='NewsBlur <%s>' % settings.HELLO_EMAIL,
|
|
|
|
to=['%s <%s>' % (user, user.email)])
|
|
|
|
msg.attach_alternative(html, "text/html")
|
|
|
|
msg.send()
|
|
|
|
|
|
|
|
logging.user(self.user, "~BB~FM~SBSending email for OPML upload: %s" % self.user.email)
|
|
|
|
|
2011-09-19 09:46:36 -07:00
|
|
|
def autologin_url(self, next=None):
|
|
|
|
return reverse('autologin', kwargs={
|
|
|
|
'username': self.user.username,
|
|
|
|
'secret': self.secret_token
|
|
|
|
}) + ('?' + next + '=1' if next else '')
|
|
|
|
|
2012-04-09 17:20:47 -07:00
|
|
|
|
2012-04-16 11:21:52 -07:00
|
|
|
|
2010-05-11 21:36:17 -04:00
|
|
|
def create_profile(sender, instance, created, **kwargs):
|
|
|
|
if created:
|
|
|
|
Profile.objects.create(user=instance)
|
2010-06-11 20:55:38 -04:00
|
|
|
else:
|
|
|
|
Profile.objects.get_or_create(user=instance)
|
2010-10-16 18:52:52 -04:00
|
|
|
post_save.connect(create_profile, sender=User)
|
|
|
|
|
|
|
|
|
|
|
|
def paypal_signup(sender, **kwargs):
|
|
|
|
ipn_obj = sender
|
2010-10-16 23:23:15 -04:00
|
|
|
user = User.objects.get(username=ipn_obj.custom)
|
2011-10-19 09:40:31 -07:00
|
|
|
try:
|
|
|
|
if not user.email:
|
|
|
|
user.email = ipn_obj.payer_email
|
|
|
|
user.save()
|
|
|
|
except:
|
|
|
|
pass
|
2010-10-23 11:20:54 -04:00
|
|
|
user.profile.activate_premium()
|
2010-11-08 12:09:55 -05:00
|
|
|
subscription_signup.connect(paypal_signup)
|
|
|
|
|
2012-02-28 17:39:02 -08:00
|
|
|
def stripe_signup(sender, full_json, **kwargs):
|
2012-07-27 12:46:37 -07:00
|
|
|
stripe_id = full_json['data']['object']['customer']
|
|
|
|
try:
|
|
|
|
profile = Profile.objects.get(stripe_id=stripe_id)
|
|
|
|
profile.activate_premium()
|
|
|
|
except Profile.DoesNotExist:
|
|
|
|
return {"code": -1, "message": "User doesn't exist."}
|
2012-02-27 21:46:34 -08:00
|
|
|
zebra_webhook_customer_subscription_created.connect(stripe_signup)
|
|
|
|
|
2010-11-08 12:09:55 -05:00
|
|
|
def change_password(user, old_password, new_password):
|
|
|
|
user_db = authenticate(username=user.username, password=old_password)
|
|
|
|
if user_db is None:
|
|
|
|
return -1
|
|
|
|
else:
|
|
|
|
user_db.set_password(new_password)
|
|
|
|
user_db.save()
|
2012-07-09 13:55:29 -07:00
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
|
|
class MSentEmail(mongo.Document):
|
|
|
|
sending_user_id = mongo.IntField()
|
|
|
|
receiver_user_id = mongo.IntField()
|
|
|
|
email_type = mongo.StringField()
|
|
|
|
date_sent = mongo.DateTimeField(default=datetime.datetime.now)
|
|
|
|
|
|
|
|
meta = {
|
|
|
|
'collection': 'sent_emails',
|
|
|
|
'allow_inheritance': False,
|
|
|
|
'indexes': [('sending_user_id', 'receiver_user_id', 'email_type')],
|
|
|
|
}
|
|
|
|
|
|
|
|
def __unicode__(self):
|
|
|
|
return "%s sent %s email to %s" % (self.sending_user_id, self.email_type, self.receiver_user_id)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def record(cls, email_type, receiver_user_id, sending_user_id):
|
|
|
|
cls.objects.create(email_type=email_type,
|
|
|
|
receiver_user_id=receiver_user_id,
|
|
|
|
sending_user_id=sending_user_id)
|
|
|
|
|