Finishing up new account email. Adding migration to save email sending preference.

This commit is contained in:
Samuel Clay 2011-09-21 17:49:26 -07:00
parent 68f091b22e
commit ed7d2bd88f
16 changed files with 223 additions and 38 deletions

View file

@ -0,0 +1,77 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'Profile.send_emails'
db.add_column('profile_profile', 'send_emails', self.gf('django.db.models.fields.BooleanField')(default=True), keep_default=False)
def backwards(self, orm):
# Deleting field 'Profile.send_emails'
db.delete_column('profile_profile', 'send_emails')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'profile.profile': {
'Meta': {'object_name': 'Profile'},
'collapsed_folders': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
'feed_pane_size': ('django.db.models.fields.IntegerField', [], {'default': '240'}),
'hide_mobile': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_premium': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_seen_ip': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
'last_seen_on': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'preferences': ('django.db.models.fields.TextField', [], {'default': "'{}'"}),
'secret_token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
'send_emails': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'timezone': ('vendor.timezones.fields.TimeZoneField', [], {'default': "'America/New_York'", 'max_length': '100'}),
'tutorial_finished': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'profile'", 'unique': 'True', 'to': "orm['auth.User']"}),
'view_settings': ('django.db.models.fields.TextField', [], {'default': "'{}'"})
}
}
complete_apps = ['profile']

View file

@ -23,6 +23,7 @@ from vendor.paypal.standard.ipn.signals import subscription_signup
class Profile(models.Model):
user = models.OneToOneField(User, unique=True, related_name="profile")
is_premium = models.BooleanField(default=False)
send_emails = models.BooleanField(default=True)
preferences = models.TextField(default="{}")
view_settings = models.TextField(default="{}")
collapsed_folders = models.TextField(default="[]")
@ -49,6 +50,8 @@ class Profile(models.Model):
self.is_premium = True
self.save()
self.send_new_premium_email()
subs = UserSubscription.objects.filter(user=self.user)
for sub in subs:
sub.active = True
@ -101,8 +104,8 @@ NewsBlur""" % {'user': self.user.username, 'feeds': subs.count()}
stale_feeds = list(set([f.feed.pk for f in stale_feeds]))
self.queue_new_feeds(new_feeds=stale_feeds)
def mail_new_account(self):
if not self.user.email:
def send_new_user_email(self):
if not self.user.email or not self.send_emails:
return
user = self.user
@ -115,8 +118,8 @@ NewsBlur""" % {'user': self.user.username, 'feeds': subs.count()}
msg.attach_alternative(html, "text/html")
msg.send()
def mail_new_premium(self):
if not self.user.email:
def send_new_premium_email(self):
if not self.user.email or not self.send_emails:
return
user = self.user

View file

@ -13,7 +13,7 @@ from utils.user_functions import ajax_login_required
from apps.profile.models import Profile, change_password
from apps.reader.models import UserSubscription
SINGLE_FIELD_PREFS = ('timezone','feed_pane_size','tutorial_finished','hide_mobile')
SINGLE_FIELD_PREFS = ('timezone','feed_pane_size','tutorial_finished','hide_mobile','send_emails',)
SPECIAL_PREFERENCES = ('old_password', 'new_password',)
@ajax_login_required

View file

@ -105,6 +105,7 @@ class SignupForm(forms.Form):
new_user.save()
new_user = authenticate(username=self.cleaned_data['username'],
password=self.cleaned_data['password'])
new_user.profile.send_new_user_email()
return new_user

View file

@ -133,19 +133,24 @@ def logout(request):
return HttpResponseRedirect(reverse('index'))
def autologin(request, username, secret):
next = request.GET.get('next', '')
if not username or not secret:
return HttpResponseForbidden()
profile = Profile.objects.filter(user__username=username, secret_token=secret)
if profile:
user = profile[0].user
user.backend = settings.AUTHENTICATION_BACKENDS[0]
login_user(request, user)
logging.user(user, "~FG~BB~SKAuto-Login~FW")
else:
if not profile:
return HttpResponseForbidden()
user = profile[0].user
user.backend = settings.AUTHENTICATION_BACKENDS[0]
login_user(request, user)
logging.user(user, "~FG~BB~SKAuto-Login. Next stop: %s~FW" % (next if next else 'Homepage',))
return HttpResponseRedirect(reverse('index') + request.GET.get('next', ''))
if next:
next = '?next=' + next
return HttpResponseRedirect(reverse('index') + next)
@json.json_view
def load_feeds(request):

6
fabfile.py vendored
View file

@ -89,6 +89,12 @@ def deploy_full():
run('curl -s http://www.newsblur.com/m/ > /dev/null')
compress_media()
@roles('web')
def restart_gunicorn():
with cd(env.NEWSBLUR_PATH):
with settings(warn_only=True):
run('sudo supervisorctl restart gunicorn')
@roles('web')
def staging():
with cd('~/staging'):

View file

@ -181,10 +181,12 @@ blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
- (void)failLoadingFeed:(ASIHTTPRequest *)request {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[appDelegate.navigationController
popToViewController:[appDelegate.navigationController.viewControllers
objectAtIndex:0]
animated:YES];
if (self.feedPage <= 1) {
[appDelegate.navigationController
popToViewController:[appDelegate.navigationController.viewControllers
objectAtIndex:0]
animated:YES];
}
[NewsBlurAppDelegate informError:[request error]];
}

View file

@ -538,7 +538,7 @@
ALWAYS_SEARCH_USER_PATHS = YES;
CODE_SIGN_ENTITLEMENTS = Entitlements.entitlements;
CODE_SIGN_IDENTITY = "iPhone Distribution";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: Samuel Clay";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
@ -567,7 +567,7 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CODE_SIGN_ENTITLEMENTS = Entitlements.entitlements;
CODE_SIGN_IDENTITY = "iPhone Distribution";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: Samuel Clay";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
COPY_PHASE_STRIP = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = NewsBlur_Prefix.pch;

View file

@ -20,7 +20,11 @@ NEWSBLUR.Modal.prototype = {
'onOpen': function (dialog) {
dialog.overlay.fadeIn(200, function () {
dialog.container.fadeIn(200);
dialog.data.fadeIn(200);
dialog.data.fadeIn(200, function() {
if (self.options.onOpen) {
self.options.onOpen();
}
});
setTimeout(function() {
$(window).resize();
});
@ -28,6 +32,9 @@ NEWSBLUR.Modal.prototype = {
},
'onShow': function(dialog) {
$('#simplemodal-container').corner('6px');
if (self.options.onShow) {
self.options.onShow();
}
},
'onClose': function(dialog, callback) {
dialog.data.hide().empty().remove();

View file

@ -104,6 +104,7 @@
this.load_recommended_feeds();
this.setup_dashboard_graphs();
this.setup_howitworks_hovers();
this.load_email_optout();
};
NEWSBLUR.Reader.prototype = {
@ -308,6 +309,13 @@
}
},
load_email_optout: function() {
var next = $.getQueryString('next');
if (next == 'optout') {
this.open_account_modal({'animate_email': true});
}
},
animate_progress_bar: function($bar, seconds, percentage) {
var self = this;
percentage = percentage || 0;
@ -2296,6 +2304,10 @@
mark_feed_as_read: function(feed_id) {
feed_id = feed_id || this.active_feed;
if (this.flags['river_view']) {
return;
}
this.mark_feed_as_read_update_counts(feed_id);
this.model.mark_feed_as_read([feed_id]);
@ -3682,8 +3694,8 @@
NEWSBLUR.preferences = new NEWSBLUR.ReaderPreferences();
},
open_account_modal: function() {
NEWSBLUR.account = new NEWSBLUR.ReaderAccount();
open_account_modal: function(options) {
NEWSBLUR.account = new NEWSBLUR.ReaderAccount(options);
},
open_feedchooser_modal: function() {
@ -6162,6 +6174,10 @@
e.preventDefault();
self.show_next_feed(-1);
});
$document.bind('keydown', 'shift+a', function(e) {
e.preventDefault();
self.mark_feed_as_read();
});
$document.bind('keydown', 'left', function(e) {
e.preventDefault();
self.switch_taskbar_view_direction(-1);

View file

@ -1,5 +1,10 @@
NEWSBLUR.ReaderAccount = function(options) {
var defaults = {};
var defaults = {
'animate_email': false,
'onOpen': _.bind(function() {
this.animate_email();
}, this)
};
this.options = $.extend({}, defaults, options);
this.model = NEWSBLUR.AssetModel.reader();
@ -18,6 +23,7 @@ _.extend(NEWSBLUR.ReaderAccount.prototype, {
this.$modal.bind('click', $.rescope(this.handle_click, this));
this.handle_change();
this.select_preferences();
},
make_modal: function() {
@ -89,6 +95,25 @@ _.extend(NEWSBLUR.ReaderAccount.prototype, {
$.make('div', { className: 'NB-preference-sublabel' }, 'Download this XML file as a backup')
])
]),
$.make('div', { className: 'NB-preference NB-preference-emails' }, [
$.make('div', { className: 'NB-preference-options' }, [
$.make('div', [
$.make('input', { id: 'NB-preference-emails-1', type: 'radio', name: 'send_emails', value: 'true' }),
$.make('label', { 'for': 'NB-preference-emails-1' }, [
'Mail me the infrequent email'
])
]),
$.make('div', [
$.make('input', { id: 'NB-preference-emails-2', type: 'radio', name: 'send_emails', value: 'false' }),
$.make('label', { 'for': 'NB-preference-emails-2' }, [
'Never ever send me an email'
])
])
]),
$.make('div', { className: 'NB-preference-label'}, [
'Emails'
])
]),
$.make('div', { className: 'NB-modal-submit' }, [
$.make('input', { type: 'submit', disabled: 'true', className: 'NB-modal-submit-green NB-disabled', value: 'Change what you like above...' }),
' or ',
@ -102,6 +127,31 @@ _.extend(NEWSBLUR.ReaderAccount.prototype, {
]);
},
animate_email: function() {
if (this.options.animate_email) {
_.delay(_.bind(function() {
var $emails = $('.NB-preference-emails', this.$modal);
var bgcolor = $emails.css('backgroundColor');
$emails.css('backgroundColor', bgcolor).animate({
'backgroundColor': 'orange'
}, {
'queue': false,
'duration': 1200,
'easing': 'easeInQuad',
'complete': function() {
$emails.animate({
'backgroundColor': bgcolor
}, {
'queue': false,
'duration': 650,
'easing': 'easeOutQuad'
});
}
});
}, this), 200);
}
},
close_and_load_preferences: function() {
this.close(function() {
NEWSBLUR.reader.open_preferences_modal();
@ -122,11 +172,22 @@ _.extend(NEWSBLUR.ReaderAccount.prototype, {
$.modal.close();
});
},
select_preferences: function() {
var pref = this.model.preference;
$('input[name=send_emails]', this.$modal).each(function() {
console.log(["pref", pref('send_emails'), $(this).val()]);
if ($(this).val() == ""+pref('send_emails')) {
$(this).attr('checked', true);
return false;
}
});
},
serialize_preferences: function() {
var preferences = {};
$('input[type=radio]:checked, select, input', this.$modal).each(function() {
$('input[type=radio]:checked, select, input[type=text], input[type=password]', this.$modal).each(function() {
var name = $(this).attr('name');
var preference = preferences[name] = $(this).val();
if (preference == 'true') preferences[name] = true;
@ -145,6 +206,8 @@ _.extend(NEWSBLUR.ReaderAccount.prototype, {
$('.NB-preference-error', this.$modal).text('');
$('input[type=submit]', this.$modal).val('Saving...').attr('disabled', true).addClass('NB-disabled');
console.log(["form['send_emails']", form['send_emails']]);
this.model.preference('send_emails', form['send_emails']);
this.model.save_account_settings(form, function(data) {
if (data.code == -1) {
$('.NB-preference-username .NB-preference-error', this.$modal).text(data.message);

View file

@ -36,6 +36,7 @@
'hide_story_changes' : 1,
'feed_view_single_story' : 0,
'animations' : true,
'send_emails' : {{ user_profile.send_emails|yesno:"true,false" }},
'view_settings' : {},
'collapsed_folders' : [],
'story_styling' : 'sans-serif',

View file

@ -10,12 +10,12 @@ Stay up to date and in touch with me, yr. developer, in a few different ways:
* Follow @newsblur on Twitter: http://twitter.com/newsblur
* Follow the constantly evolving source code on GitHub: http://github.com/samuelclay
{% block resources_header %}There are a few resources that would make sense to follow if you end up loving NewsBlur:{% endblock resources_header %}
{% block resources_header %}There are a few resources you can use if you end up loving NewsBlur:{% endblock resources_header %}
* Read the NewsBlur Blog: http://blog.newsblur.com
* Get support on NewsBlur's Get Satisfaction: http://getsatisfaction.com/newsblur
* Download the free NewsBlur iPhone app: http://itunes.com/newsblur
* Download the free NewsBlur iPhone app: [Coming soon -- it's in review on the App Store]
-----------------------------------------------------------------------------
Don't want to be notified about anything NewsBlur related? Opt-out of emails from NewsBlur: http://www.newsblur.com{{ user.profile.autologin_url }}?optout=1.
Don't want to be notified about anything NewsBlur related? Opt-out of emails from NewsBlur: http://www.newsblur.com{{ user.profile.autologin_url }}?next=optout

View file

@ -31,17 +31,17 @@
<p style="line-height:20px;">Stay up to date and in touch with me, yr. developer, in a few different ways:</p>
<p style="line-height: 20px;">
<ul style="list-style: none;">
<li><a href="http://twitter.com/samuelclay" style="text-decoration:none"><img src="http://www.newsblur.com/media/img/reader/twitter_icon_2.png" style="width:16px;height:16px;vertical-align:top;"> Follow @samuelclay on Twitter</a>.</li>
<li><a href="http://twitter.com/newsblur" style="text-decoration:none"><img src="http://www.newsblur.com/media/img/reader/twitter.png" style="width:16px;height:16px;vertical-align:top;"> Follow @newsblur on Twitter</a>.</li>
<li><a href="http://github.com/samuelclay" style="text-decoration:none"><img src="http://www.newsblur.com/media/img/reader/github_icon.png" style="width:16px;height:16px;vertical-align:top;"> Follow the constantly evolving source code on GitHub</a>.</li>
<li style="line-height:22px;"><a href="http://twitter.com/samuelclay" style="text-decoration:none"><img src="http://www.newsblur.com/media/img/reader/twitter_icon_2.png" style="width:16px;height:16px;vertical-align:top;"> Follow @samuelclay on Twitter</a>.</li>
<li style="line-height:22px;"><a href="http://twitter.com/newsblur" style="text-decoration:none"><img src="http://www.newsblur.com/media/img/reader/twitter.png" style="width:16px;height:16px;vertical-align:top;"> Follow @newsblur on Twitter</a>.</li>
<li style="line-height:22px;"><a href="http://github.com/samuelclay" style="text-decoration:none"><img src="http://www.newsblur.com/media/img/reader/github_icon.png" style="width:16px;height:16px;vertical-align:top;"> Follow the constantly evolving source code on GitHub</a>.</li>
</ul>
</p>
<p style="line-height: 20px;">{% block resources_header %}There are a few resources that would make sense to follow if you end up loving NewsBlur:{% endblock resources_header %}</p>
<p style="line-height: 20px;">{% block resources_header %}There are a few resources you can use if you end up loving NewsBlur:{% endblock resources_header %}</p>
<p style="line-height: 20px;">
<ul style="list-style: none;">
<li><a href="http://blog.newsblur.com" style="text-decoration:none"><img src="http://www.newsblur.com/media/img/favicon.png" style="width:16px;height:16px;vertical-align:top;"> Read the NewsBlur Blog</a>.</li>
<li><a href="http://getsatisfaction.com/newsblur" style="text-decoration:none"><img src="http://www.newsblur.com/media/img/reader/getsatisfaction.png" style="width:16px;height:16px;vertical-align:top;"> Get support on NewsBlur's Get Satisfaction</a>.</li>
<li><a href="http://itunes.com/newsblur" style="text-decoration:none"><img src="http://nb.local.host:8000/media/img/reader/iphone_icon.png" style="width:16px;height:16px;vertical-align:top;"> Download the free NewsBlur iPhone app</a>.</li>
<li style="line-height:22px;"><a href="http://blog.newsblur.com" style="text-decoration:none"><img src="http://www.newsblur.com/media/img/favicon.png" style="width:16px;height:16px;vertical-align:top;"> Read the NewsBlur Blog</a>.</li>
<li style="line-height:22px;"><a href="http://getsatisfaction.com/newsblur" style="text-decoration:none"><img src="http://www.newsblur.com/media/img/reader/getsatisfaction.png" style="width:16px;height:16px;vertical-align:top;"> Get support on NewsBlur's Get Satisfaction</a>.</li>
<li style="line-height:22px;"><img src="http://nb.local.host:8000/media/img/reader/iphone_icon.png" style="width:16px;height:16px;vertical-align:top;"> [Coming soon -- it's in review on the App Store]</li>
</ul>
</p>
</td>
@ -55,7 +55,7 @@
<tr>
<td>
<p style="font-size:12px;">
Don't want to be notified about anything NewsBlur related? <a href="http://www.newsblur.com{{ user.profile.autologin_url }}?optout=1" style="color:#1C4D84; text-decoration:none;">Opt-out of emails from NewsBlur</a>.
Don't want to be notified about anything NewsBlur related? <a href="http://www.newsblur.com{{ user.profile.autologin_url }}?next=optout" style="color:#1C4D84; text-decoration:none;">Opt-out of emails from NewsBlur</a>.
</p>
</td>
</tr>
@ -63,7 +63,7 @@
</td>
</tr>
<tr>
<td align="center">
<td align="center" style="padding-top: 12px;">
<a href="http://www.newsblur.com{{ user.profile.autologin_url }}"><img alt="NewsBlur" src="http://www.newsblur.com/media/img/logo_newsblur.png" title="NewsBlur" style="width:312px;height:55px;border: none;"></a>
</td>
</tr>

View file

@ -4,4 +4,8 @@
OK, firstly, thank you for trying out new software. It's not quite what you're used to, but you might find NewsBlur to be a perfect fit. There's a lot packed into a minimal interface.
Spend a few days trying out NewsBlur. I hope you end up loving it.{% endblock body %}
Spend a few days trying out NewsBlur. I hope you end up loving it.
If you really do love using NewsBlur you can purchase a fancy new premium account for only $1/month. You get to support an independent developer, help feed his dog, and contribute to the long-term health of NewsBlur.
(Plus, among other cool premium-only features, all of your sites will update more often. Going premium supports 100% genuine hard-work and you'll make me a very happy developer.){% endblock body %}

View file

@ -4,6 +4,6 @@
<p style="font-size: 37px; color:#555555; margin-top: 18px;margin-bottom: 10px;padding-top:6px;">Welcome to NewsBlur, {{ user.username }}.</p>
<p style="line-height: 20px;">OK, firstly, thank you for trying out new software. It's not quite what you're used to, but you might find NewsBlur to be a perfect fit. There's a lot packed into a minimal interface.</p>
<p style="line-height: 20px;">Spend a few days trying out NewsBlur. I hope you end up loving it.</p>
<p style="line-height: 20px;">If you really do love using NewsBlur you can purchase a fancy new <b>premium account</b> for only $1/month. You get to support an independent developer, help feed his dog, and contribute to the long-term health of NewsBlur.</p>
<p style="line-height: 20px;">(Plus, among other cool premium-only features, all of your sites will update more often. Going premium supports 100% genuine hard-work and you'll make me a very happy developer.)</p>
{% endblock %}
{% block resources_header %}There are a few resources that would make sense to follow if you end up loving NewsBlur:{% endblock resources_header %}