Google Reader import now allows you to create an account after you import your feeds. Also highlighted on front page.

This commit is contained in:
Samuel Clay 2010-07-05 17:11:04 -04:00
parent 5735fe5b6a
commit 81f5d0d1e0
8 changed files with 250 additions and 57 deletions

View file

@ -0,0 +1,76 @@
# 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 'OAuthToken.session_id'
db.add_column('feed_import_oauthtoken', 'session_id', self.gf('django.db.models.fields.CharField')(max_length=50, null=True, blank=True), keep_default=False)
# Changing field 'OAuthToken.user'
db.alter_column('feed_import_oauthtoken', 'user_id', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.User'], unique=True, null=True, blank=True))
def backwards(self, orm):
# Deleting field 'OAuthToken.session_id'
db.delete_column('feed_import_oauthtoken', 'session_id')
# Changing field 'OAuthToken.user'
db.alter_column('feed_import_oauthtoken', 'user_id', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.User'], unique=True))
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': {'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', 'blank': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
'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': {'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'})
},
'feed_import.oauthtoken': {
'Meta': {'object_name': 'OAuthToken'},
'access_token': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
'access_token_secret': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'request_token': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
'request_token_secret': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
'session_id': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'})
}
}
complete_apps = ['feed_import']

View file

@ -9,7 +9,8 @@ from utils import json
import utils.opml as opml
class OAuthToken(models.Model):
user = models.OneToOneField(User)
user = models.OneToOneField(User, null=True, blank=True)
session_id = models.CharField(max_length=50, null=True, blank=True)
request_token = models.CharField(max_length=50)
request_token_secret = models.CharField(max_length=50)
access_token = models.CharField(max_length=50)
@ -81,7 +82,8 @@ class GoogleReaderImporter(Importer):
folders = defaultdict(list)
for item in self.feeds:
folders = self.process_item(item, folders)
print dict(folders)
print "Google Reader import"
# print dict(folders)
self.rearrange_folders(folders)
print self.subscription_folders
UserSubscriptionFolders.objects.create(user=self.user,

View file

@ -4,5 +4,6 @@ from apps.feed_import import views
urlpatterns = patterns('apps.feed_import.views',
url(r'^opml_upload$', views.opml_upload, name='opml-upload'),
url(r'^authorize/$', views.reader_authorize, name='opml-reader-authorize'),
url(r'^callback/$', views.reader_callback, name='opml-reader-callback')
url(r'^callback/$', views.reader_callback, name='opml-reader-callback'),
url(r'^signup/$', views.import_signup, name='import-signup')
)

View file

@ -1,14 +1,17 @@
# -*- coding: utf-8 -*-
from apps.reader.models import UserSubscription
from utils import json
from apps.feed_import.models import OAuthToken, OPMLImporter, GoogleReaderImporter
from django.contrib.auth.models import User
import urlparse
import oauth2 as oauth
from django.contrib.sites.models import Site
from django.http import HttpResponse, HttpResponseRedirect
from django.conf import settings
from django.core.urlresolvers import reverse
import urlparse
import oauth2 as oauth
from django.template import RequestContext
from django.contrib.auth import login as login_user
from django.shortcuts import render_to_response
from apps.reader.forms import SignupForm
from apps.reader.models import UserSubscription
from apps.feed_import.models import OAuthToken, OPMLImporter, GoogleReaderImporter
from utils import json
def opml_upload(request):
@ -39,22 +42,30 @@ def reader_authorize(request):
oauth_key = settings.OAUTH_KEY
oauth_secret = settings.OAUTH_SECRET
scope = "http://www.google.com/reader/api"
request_token_url = "https://www.google.com/accounts/OAuthGetRequestToken?scope=%s&oauth_callback=http://%s%s" % (
scope,
Site.objects.get_current().domain,
reverse('opml-reader-callback'),
)
request_token_url = ("https://www.google.com/accounts/OAuthGetRequestToken?"
"scope=%s&oauth_callback=http://%s%s") % (
scope,
Site.objects.get_current().domain,
reverse('opml-reader-callback'),
)
authorize_url = 'https://www.google.com/accounts/OAuthAuthorizeToken'
# Grab request token from Google's OAuth
consumer = oauth.Consumer(oauth_key, oauth_secret)
client = oauth.Client(consumer)
resp, content = client.request(request_token_url, "GET")
request_token = dict(urlparse.parse_qsl(content))
OAuthToken.objects.filter(user=request.user).delete()
OAuthToken.objects.create(user=request.user,
request_token=request_token['oauth_token'],
request_token_secret=request_token['oauth_token_secret'])
# Save request token and delete old tokens
auth_token_dict = dict(request_token=request_token['oauth_token'],
request_token_secret=request_token['oauth_token_secret'])
if request.user.is_authenticated():
OAuthToken.objects.filter(user=request.user).delete()
auth_token_dict['user'] = request.user
else:
OAuthToken.objects.filter(session_id=request.session.session_key).delete()
auth_token_dict['session_id'] = request.session.session_key
OAuthToken.objects.create(**auth_token_dict)
redirect = "%s?oauth_token=%s" % (authorize_url, request_token['oauth_token'])
return HttpResponseRedirect(redirect)
@ -62,33 +73,59 @@ def reader_authorize(request):
def reader_callback(request):
access_token_url = 'https://www.google.com/accounts/OAuthGetAccessToken'
consumer = oauth.Consumer(settings.OAUTH_KEY, settings.OAUTH_SECRET)
user_token = OAuthToken.objects.get(user=request.user)
if request.user.is_authenticated():
user_token = OAuthToken.objects.get(user=request.user)
else:
user_token = OAuthToken.objects.get(session_id=request.session.session_key)
# Authenticated in Google, so verify and fetch access tokens
token = oauth.Token(user_token.request_token, user_token.request_token_secret)
token.set_verifier(request.GET['oauth_verifier'])
client = oauth.Client(consumer, token)
resp, content = client.request(access_token_url, "POST")
access_token = dict(urlparse.parse_qsl(content))
user_token.access_token = access_token['oauth_token']
user_token.access_token_secret = access_token['oauth_token_secret']
user_token.save()
# Fetch imported feeds on next page load
request.session['import_from_google_reader'] = True
return HttpResponseRedirect(reverse('index'))
if request.user.is_authenticated():
return HttpResponseRedirect(reverse('index'))
return HttpResponseRedirect(reverse('import-signup'))
def import_from_google_reader(user):
scope = "http://www.google.com/reader/api"
sub_url = "%s/0/subscription/list" % scope
user_tokens = OAuthToken.objects.filter(user=user)
if user_tokens.count():
user_token = user_tokens[0]
consumer = oauth.Consumer(settings.OAUTH_KEY, settings.OAUTH_SECRET)
token = oauth.Token(user_token.access_token, user_token.access_token_secret)
client = oauth.Client(consumer, token)
resp, content = client.request(sub_url, 'GET')
reader_importer = GoogleReaderImporter(content, user)
return reader_importer.process()
if user.is_authenticated():
user_tokens = OAuthToken.objects.filter(user=user)
if user_tokens.count():
user_token = user_tokens[0]
consumer = oauth.Consumer(settings.OAUTH_KEY, settings.OAUTH_SECRET)
token = oauth.Token(user_token.access_token, user_token.access_token_secret)
client = oauth.Client(consumer, token)
resp, content = client.request(sub_url, 'GET')
reader_importer = GoogleReaderImporter(content, user)
return reader_importer.process()
def import_signup(request):
if request.method == "POST":
signup_form = SignupForm(prefix='signup', data=request.POST)
if signup_form.is_valid():
new_user = signup_form.save()
user_token = OAuthToken.objects.get(session_id=request.session.session_key)
user_token.user = new_user
user_token.save()
login_user(request, new_user)
return HttpResponseRedirect(reverse('index'))
else:
signup_form = SignupForm(prefix='signup')
return render_to_response('import/signup.xhtml', {
'signup_form': signup_form,
}, context_instance=RequestContext(request))

View file

@ -81,6 +81,27 @@ a img {
overflow: hidden;
}
.NB-account .NB-import-signup {
float: left;
width: 186px;
padding: 0 0 64px 0;
margin: 0 50px 0 12px;
height: 206px;
overflow: hidden;
}
.NB-account .NB-import-signup-text {
text-align: center;
width: 200px;
}
.NB-account .NB-import-signup-text h3 {
margin-top: 48px;
color: #20843D;
}
.NB-account .NB-import-signup-text p {
color: #636363;
}
.NB-account .NB-signup-optional {
float: right;
text-transform: uppercase;
@ -93,6 +114,10 @@ a img {
display: none;
}
.NB-account .NB-import-signup {
height: auto;
}
.NB-account .NB-signup .NB-signup-google {
margin: 12px auto;
display: block;

View file

@ -27,7 +27,9 @@
</script>
{% compressed_css 'all' %}
{% compressed_js 'all' %}
{% block head_js %}
{% compressed_js 'all' %}
{% endblock head_js %}
<script type="text/javascript" charset="utf-8">
{% if request.user.is_authenticated %}

View file

@ -0,0 +1,73 @@
{% extends 'base.html' %}
{% block head_js %}{% endblock head_js %}
{% block content %}
<div id="NB-splash">
<div class="NB-account">
{% if not user.is_authenticated %}
<div class="NB-module">
<h5>
<div class="NB-module-header-signup">Signup</div>
</h5>
<div class="NB-import-signup">
<form method="post" action="{% url import-signup %}">
<div>
{{ signup_form.signup_username.label_tag }}
{{ signup_form.signup_username }}
</div>
<div class="">
<div class="NB-signup-optional">Optional</div>
{{ signup_form.signup_password.label_tag }}
{{ signup_form.signup_password }}
</div>
<div class="">
<div class="NB-signup-optional">Optional</div>
{{ signup_form.email.label_tag }}
{{ signup_form.email }}
</div>
<input name="submit" type="submit" value="create account" />
<input type="hidden" name="next" value="/" />
</form>
{% if signup_form.errors %}
{% for field, error in signup_form.errors.items %}
{{ error }}
{% endfor %}
{% endif %}
</div>
<div class="NB-signup NB-import-signup-text">
<h3>Almost There!</h3>
<p>To complete the import,<br />just choose a username.</p>
</div>
</div>
{% endif %}
</div>
<div class="NB-splash-info">
<ul class="NB-splash-links">
<li class="NB-splash-link">Hand-crafted by: <a href="http://www.samuelclay.com">Samuel Clay</a></li>
<li class="NB-splash-link">Write to me: <a href="mailto:samuel@ofbrooklyn.com">samuel@ofbrooklyn.com</a></li>
<li class="NB-splash-link">Twitter, Twitter: <a href="http://twitter.com/samuelclay">@samuelclay</a></li>
</ul>
<div class="NB-splash-title"></div>
</div>
</div>
{% endblock %}

View file

@ -1,23 +0,0 @@
{% extends 'base.html' %}
{% block content %}
<form action="{% url apps.opml_import.views.process %}" method="post" class="opml_import_form">
<div class="section">
Paste the OPML file here:
<textarea class="opml_file" name="opml"></textarea>
</div>
<div class="section">
<input type="radio" name="merge_option" value="overwrite" id="merge_choice__overwrite" checked="checked"></input>
<label for="merge_choice__overwrite">Overwrite existing feeds</label>
</div>
<div class="section">
<input type="radio" name="merge_option" value="merge" id="merge_choice__merge"></input>
<label for="merge_choice__merge">Merge into existing feeds</label>
</div>
<input type="submit" value="Import OPML Feeds" />
</form>
{% endblock %}