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 %} + +{% 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))