diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0d20b64
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..498054c
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,97 @@
+google_contacts
+===============
+
+Allows you to retrieve the contacts from the Gmail account of a user of your
+site, via Google's APIs.
+
+Dependencies:
+
+- gdata-python-client
+ http://code.google.com/p/gdata-python-client/
+
+- json_response
+ http://www.djangosnippets.org/snippets/154/
+ (save this snippet in a file on your PYTHONPATH called json_response.py)
+
+- django-picklefield
+ http://github.com/shrubberysoft/django-picklefield
+ (save the fields.py file on your PYTHONPATH and rename it picklefield.py)
+
+Installation instructions:
+
+1. Place the google_contacts directory somewhere in your PYTHONPATH.
+
+2. Ensure that all the dependencies (listed above) are installed.
+
+3. Define the following variables in your project's settings.py file:
+
+-----------------------------------------------------------------------------
+
+GOOGLE_COOKIE_CONSENT = 'google_token_consent'
+
+(the value can be whatever you want, as long as it's a unique cookie name
+within your project)
+
+GOOGLE_REDIRECT_SESSION_VAR = 'google_contacts_redirect'
+
+(the value can be whatever you want, as long as it's a unique session var
+within your project)
+
+GOOGLE_REDIRECT_BASE_URL = 'http://localhost:8000'
+(should be whatever the base URL of your site is)
+
+-----------------------------------------------------------------------------
+
+4. Use code similar to the following, in the view for which you want to
+ display the contact import functionality:
+
+-----------------------------------------------------------------------------
+
+import gdata.contacts.service
+
+from django.conf import settings
+from django.shortcuts import render_to_response
+from django.template import RequestContext
+
+from google_contacts.utils import google_get_state, google_import
+
+def test_page(request):
+ request.session[settings.GOOGLE_REDIRECT_SESSION_VAR] = request.path
+
+ google_state = google_get_state(request)
+ gcs = gdata.contacts.service.ContactsService()
+ google_contacts = google_import(request, gcs, cache=True)
+
+ return render_to_response('test_page.html', {
+ 'google_state': google_state,
+ 'google_contacts': google_contacts
+ }, context_instance=RequestContext(request))
+
+-----------------------------------------------------------------------------
+
+5. Use code similar to the following, in the template for which you want to
+ display the contact import functionality:
+
+-----------------------------------------------------------------------------
+
+{% load google_contacts %}
+
+
+{% if google_state %}
+ Using imported contacts from Gmail [stop using]
+{% else %}
+ Import contacts from Gmail
+{% endif %}
+
+
+{% if google_contacts %}
+
+ {% for contact in google_contacts %}
+ - {{ contact }}
+ {% endfor %}
+
+{% endif %}
+
+-----------------------------------------------------------------------------
diff --git a/__init__.py b/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/models.py b/models.py
new file mode 100644
index 0000000..ba0cbfa
--- /dev/null
+++ b/models.py
@@ -0,0 +1,23 @@
+from uuid import uuid4
+
+from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.models import ContentType
+from django.db import models
+
+from picklefield import PickledObjectField
+
+class ActionState(models.Model):
+ """
+ A temporary store for the state of a user form entry.
+
+ The typical usage of this would be for an action where the user needs
+ to leave and return to the page, for instance in an iframe-busting
+ auth process.
+ """
+ uuid = models.CharField(max_length=32, default=lambda: uuid4().hex, primary_key=True)
+ action_type = models.ForeignKey(ContentType, null=True)
+ action_id = models.PositiveIntegerField(null=True)
+ action = generic.GenericForeignKey('action_type', 'action_id')
+ data = PickledObjectField()
+ created = models.DateTimeField(auto_now_add=True)
+ modified = models.DateTimeField(auto_now=True)
diff --git a/templatetags/__init__.py b/templatetags/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/templatetags/google_contacts.py b/templatetags/google_contacts.py
new file mode 100644
index 0000000..8a7ea6a
--- /dev/null
+++ b/templatetags/google_contacts.py
@@ -0,0 +1,19 @@
+from gdata.auth import GenerateAuthSubUrl
+
+from django import template
+from django.conf import settings
+from django.core.urlresolvers import reverse
+
+from ..utils import google_get_state
+
+register = template.Library()
+
+@register.simple_tag
+def google_auth_url(request):
+ state = google_get_state(request)
+ if not state:
+ next = '%s%s' % (settings.GOOGLE_REDIRECT_BASE_URL, reverse('google_contacts_login'))
+ scope = 'http://www.google.com/m8/feeds/'
+ return GenerateAuthSubUrl(next, scope, secure=False, session=True)
+ else:
+ return reverse('google_contacts_logout')
diff --git a/urls.py b/urls.py
new file mode 100644
index 0000000..7e305df
--- /dev/null
+++ b/urls.py
@@ -0,0 +1,11 @@
+from django.conf.urls.defaults import *
+
+from views import google_get_state_token, google_login, google_logout
+
+urlpatterns = patterns('',
+
+ url(r'^get-state-token/(?P\d+)/(?P\d+)/$', google_get_state_token, name='google_contacts_get_state_token'),
+ url(r'^login/$', google_login, name='google_contacts_login'),
+ url(r'^logout/$', google_logout, name='google_contacts_logout'),
+
+)
diff --git a/utils.py b/utils.py
new file mode 100644
index 0000000..a98a9e0
--- /dev/null
+++ b/utils.py
@@ -0,0 +1,52 @@
+from django.conf import settings
+
+def google_import(request, gcs, cache=False):
+ """
+ Uses the given contacts service object to retrieve Google Contacts and
+ import the entries with an email address into the contacts of the
+ given user.
+
+ Returns a list of 'contact name ' strings.
+ """
+ state = google_get_state(request)
+ contacts = []
+
+ if state == 'authorized':
+ if cache and request.session.get('google_contacts_cached'):
+ return request.session.get('google_contacts_cached')
+
+ gcs.SetAuthSubToken(request.session.get(settings.GOOGLE_COOKIE_CONSENT))
+
+ entries = []
+ feed = gcs.GetContactsFeed()
+ entries.extend(feed.entry)
+ next_link = feed.GetNextLink()
+ while next_link:
+ feed = gcs.GetContactsFeed(uri=next_link.href)
+ entries.extend(feed.entry)
+ next_link = feed.GetNextLink()
+
+ for entry in entries:
+ for email in entry.email:
+ if email.primary:
+ contact = '%s <%s>' % (entry.title.text, email.address)
+ contacts.append(contact)
+
+ if cache:
+ request.session['google_contacts_cached'] = contacts
+
+ return contacts
+
+
+def google_get_state(request):
+ """
+ Get the current login state for the Google Contacts API.
+ Possible states are:
+ None - logged out
+ authorized - logged in and consent granted
+ """
+ state = None
+ if request.session.get(settings.GOOGLE_COOKIE_CONSENT):
+ state = 'authorized'
+
+ return state
diff --git a/views.py b/views.py
new file mode 100644
index 0000000..51ec1c4
--- /dev/null
+++ b/views.py
@@ -0,0 +1,34 @@
+import gdata.contacts.service
+
+from django.conf import settings
+from django.shortcuts import redirect
+
+from json_response import JsonResponse
+from models import ActionState
+
+def google_get_state_token(request, action_type_id, action_id):
+ action_state = ActionState.objects.create(**{
+ 'action_type_id': action_type_id,
+ 'action_id': action_id,
+ 'data': request.GET
+ })
+ return JsonResponse({'stat': 'ok', 'token': action_state.uuid})
+
+def google_login(request):
+ token_login = request.GET.get('token')
+
+ if token_login:
+ gcs = gdata.contacts.service.ContactsService()
+ gcs.SetAuthSubToken(token_login)
+ gcs.UpgradeToSessionToken()
+ request.session[settings.GOOGLE_COOKIE_CONSENT] = gcs.GetAuthSubToken()
+
+ return redirect(request.session.get(settings.GOOGLE_REDIRECT_SESSION_VAR))
+
+
+def google_logout(request):
+ if request.session.get(settings.GOOGLE_COOKIE_CONSENT):
+ del request.session[settings.GOOGLE_COOKIE_CONSENT]
+ if request.session.get('google_contacts_cached'):
+ del request.session['google_contacts_cached']
+ return redirect(request.session.get(settings.GOOGLE_REDIRECT_SESSION_VAR))