# HG changeset patch # User Brian Neal # Date 1309399037 0 # Node ID 345825e6dcae3fe7c3fb741ca67fa897e0b8266d # Parent acc129bfd0d365833200a1fad06c3773fcb5c51e Working on #220. Can't test locally, so committing in increments. diff -r acc129bfd0d3 -r 345825e6dcae gpp/gcalendar/admin.py --- a/gpp/gcalendar/admin.py Sun Jun 26 00:15:36 2011 +0000 +++ b/gpp/gcalendar/admin.py Thu Jun 30 01:57:17 2011 +0000 @@ -1,15 +1,31 @@ """ This file contains the automatic admin site definitions for the gcalendar application. + """ +from django.conf import settings +from django.conf.urls.defaults import * from django.contrib import admin +from django.contrib import messages +from django.core.urlresolvers import reverse from django.http import HttpResponse -from django.conf.urls.defaults import * +from django.http import HttpResponseRedirect +from django.shortcuts import render_to_response +from django.template import RequestContext + +import gdata.client from gcalendar.models import Event -from gcalendar.admin_views import google_sync +from gcalendar.forms import PasswordForm +from gcalendar.calendar import Calendar +from gcalendar.calendar import CalendarError +from gcalendar import oauth + import bio.badges +SCOPES = ['https://www.google.com/calendar/feeds/'] + + class EventAdmin(admin.ModelAdmin): list_display = ('what', 'user', 'start_date', 'where', 'date_submitted', 'status', 'is_approved', 'google_html') @@ -30,9 +46,15 @@ def get_urls(self): urls = super(EventAdmin, self).get_urls() my_urls = patterns('', - url(r'^google_sync/$', - self.admin_site.admin_view(google_sync), - name="gcalendar-google_sync") + url(r'^google_sync/$', + self.admin_site.admin_view(self.google_sync), + name="gcalendar-google_sync"), + url(r'^fetch_auth/$', + self.admin_site.admin_view(self.fetch_auth), + name="gcalendar-fetch_auth"), + url(r'^get_access_token/$', + self.admin_site.admin_view(self.get_access_token), + name="gcalendar-get_access_token"), ) return my_urls + urls @@ -57,6 +79,72 @@ approve_events.short_description = "Approve selected events" + def google_sync(self, request): + """View to synchronize approved event changes with Google calendar.""" + events = Event.pending_events.all() + messages = [] + err_msg = '' + if request.method == 'POST': + form = PasswordForm(request.POST) + if form.is_valid(): + try: + cal = Calendar(settings.GCAL_EMAIL, + form.cleaned_data['password'], + settings.GCAL_CALENDAR_ID) + cal.sync_events(events) + except CalendarError, e: + err_msg = e.msg + events = Event.pending_events.all() + form = PasswordForm() + else: + messages.append('All events processed successfully.') + events = Event.objects.none() + form = PasswordForm() + + else: + form = PasswordForm() + + return render_to_response('gcalendar/google_sync.html', { + 'current_app': self.admin_site.name, + 'messages': messages, + 'err_msg': err_msg, + 'events': events, + 'form': form, + }, + context_instance=RequestContext(request)) + + def fetch_auth(self, request): + """ + This view fetches a request token and then redirects the user to + authorize it. + + """ + callback_url = reverse('admin:gcalendar-get_access_token') + try: + auth_url = oauth.fetch_auth(request, SCOPES, callback_url) + except gdata.client.Error, e: + messages.error(request, str(e)) + return HttpResponseRedirect(reverse('admin:gcalendar-google_sync')) + else: + return HttpResponseRedirect(auth_url) + + def get_access_token(self, request): + """ + This view is called by Google after the user has authorized us access to + their data. We call into the oauth module to upgrade the oauth token to + an access token. We then save the access token in the database and + redirect back to our admin Google sync view. + + """ + try: + access_token = oauth.get_access_token(request) + except gdata.client.Error, e: + messages.error(request, str(e)) + else: + # TODO: save access token + pass + + return HttpResponseRedirect(reverse('admin:gcalendar-google_sync')) + admin.site.register(Event, EventAdmin) - diff -r acc129bfd0d3 -r 345825e6dcae gpp/gcalendar/admin_views.py --- a/gpp/gcalendar/admin_views.py Sun Jun 26 00:15:36 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -""" -Admin views for the gcalendar application. -""" -from django.shortcuts import render_to_response -from django.template import RequestContext - -from gcalendar.models import Event -from gcalendar.forms import PasswordForm -from gcalendar.calendar import Calendar -from gcalendar.calendar import CalendarError -import gcalendar.settings - - -def google_sync(request): - """View to synchronize approved event changes with Google calendar.""" - events = Event.pending_events.all() - messages = [] - err_msg = '' - if request.method == 'POST': - form = PasswordForm(request.POST) - if form.is_valid(): - try: - cal = Calendar(gcalendar.settings.EMAIL, - form.cleaned_data['password'], - gcalendar.settings.CALENDAR_ID) - cal.sync_events(events) - except CalendarError, e: - err_msg = e.msg - events = Event.pending_events.all() - form = PasswordForm() - else: - messages.append('All events processed successfully.') - events = Event.objects.none() - form = PasswordForm() - - else: - form = PasswordForm() - - return render_to_response('gcalendar/google_sync.html', { - 'messages': messages, - 'err_msg': err_msg, - 'events': events, - 'form': form, - }, - context_instance=RequestContext(request)) - diff -r acc129bfd0d3 -r 345825e6dcae gpp/gcalendar/oauth.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpp/gcalendar/oauth.py Thu Jun 30 01:57:17 2011 +0000 @@ -0,0 +1,82 @@ +""" +This module handles the OAuth integration with Google. + +""" +from __future__ import with_statement +import logging + +import gdata.gauth +from gdata.calendar_resource.client import CalendarResourceClient + +from django.conf import settings + + +logger = logging.getLogger(__name__) +USER_AGENT = 'surfguitar101-gcalendar-v1' +REQ_TOKEN_SESSION_KEY = 'gcalendar oauth request token' + + +def fetch_auth(request, scopes, callback_url): + """ + This function fetches a request token from Google and stores it in the + session. It then returns the authorization URL as a string. + + request - the HttpRequest object for the user requesting the token. The + token is stored in the session object attached to this request. + + scopes - a list of scope strings that the request token is for. See + http://code.google.com/apis/gdata/faq.html#AuthScopes + + callback_url - a string that is the URL that Google should redirect the user + to after the user has authorized our application access to their data. + + This function only supports RSA-SHA1 authentication. Settings in the Django + settings module determine the consumer key and path to the RSA private key. + """ + logger.info("fetch_auth started...") + client = CalendarResourceClient(None, source=USER_AGENT) + + with open(settings.GOOGLE_OAUTH_PRIVATE_KEY_PATH, 'r') as f: + rsa_key = f.read() + logger.info("read RSA key; now getting request token") + + request_token = client.GetOAuthToken( + scopes, + callback_url, + settings.GOOGLE_OAUTH_CONSUMER_KEY, + rsa_private_key=rsa_key) + + logger.info("received token") + request.session[REQ_TOKEN_SESSION_KEY] = request_token + + auth_url = request_token.generate_authorization_url(google_apps_domain=None) + logger.info("generated auth url '%s'", str(auth_url)) + + return str(auth_url) + + +def get_access_token(request): + """ + This function should be called after Google has sent the user back to us + after the user authorized us. We retrieve the oauth token from the request + URL and then upgrade it to an access token. We then return the access token. + + """ + logger.info("get_access_token started; retrieving saved request_token...") + + saved_token = request.session.get(REQ_TOKEN_SESSION_KEY) + if saved_token is None: + logger.error("saved request token not found in session!") + return None + + logger.info("extracting token...") + request_token = gdata.gauth.AuthorizeRequestToken(saved_token, + request.build_absolute_uri()) + + logger.info("upgrading to access token...") + + client = CalendarResourceClient(None, source=USER_AGENT) + access_token = client.GetAccessToken(request_token) + + logger.info("upgraded to access token...") + return access_token diff -r acc129bfd0d3 -r 345825e6dcae gpp/gcalendar/settings.py --- a/gpp/gcalendar/settings.py Sun Jun 26 00:15:36 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -""" -This file contains the user tweakable settings for the gcalendar application. -""" - -EMAIL = 'bgneal@gmail.com' -CALENDAR_ID = 'i81lu3fkh57sgqqenogefd9v78@group.calendar.google.com' - -# vim: ts=4 sw=4 diff -r acc129bfd0d3 -r 345825e6dcae gpp/settings.py --- a/gpp/settings.py Sun Jun 26 00:15:36 2011 +0000 +++ b/gpp/settings.py Thu Jun 30 01:57:17 2011 +0000 @@ -261,6 +261,16 @@ OEMBED_MAXWIDTH = 480 OEMBED_MAXHEIGHT = 295 +# GCalendar settings + +GCAL_EMAIL = 'bgneal@gmail.com' +GCAL_CALENDAR_ID = 'i81lu3fkh57sgqqenogefd9v78@group.calendar.google.com' + +# Google OAuth settings + +GOOGLE_OAUTH_CONSUMER_KEY = local_settings.GOOGLE_OAUTH_CONSUMER_KEY +GOOGLE_OAUTH_PRIVATE_KEY_PATH = local_settings.GOOGLE_OAUTH_PRIVATE_KEY_PATH + ####################################################################### # Configure Logging #######################################################################