# Copyright 2008 Ryan E. Pfister # # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, # either express # or implied. See the License for the specific language governing permissions and limitations under the License. import wsgiref.handlers import urllib from google.appengine.ext import webapp from google.appengine.api import users import gdata.service import gdata.alt.appengine import cgi from google.appengine.ext import db import atom import gdata.contacts import gdata.contacts.service # Change the value of HOST_NAME to the name given to point to your app. HOST_NAME = 'fullcontactsearch.appspot.com' class StoredToken(db.Model): user_email = db.StringProperty(required=True) session_token = db.StringProperty(required=True) target_url = db.StringProperty(required=True) class Fetcher(webapp.RequestHandler): # Initialize some global variables we will use def __init__(self): # Stores the page's current user self.current_user = None # Stores the token_scope information self.token_scope = None # Stores the Google Data Client self.client = None # The one time use token value from the URL after the AuthSub redirect. self.token = None #Search query string self.q = None #Whether to show the search box self.show_search = True def get(self): # Write our pages title self.response.out.write(""" Full Contact Search: Search all fields in your Gmail Address Book, not just name and e-mail""") # Get the current user self.current_user = users.GetCurrentUser() self.response.out.write('') self.PrintSignInOutLink(); for param in self.request.query.split('&'): # Get the token scope variable we specified when generating the URL if param.startswith('auth_sub_scopes'): self.token_scope = urllib.unquote_plus(param.split('=')[1]) # Google Data will return a token, get that elif param.startswith('token'): self.token = param.split('=')[1] elif param.startswith('q'): self.q = param.split('=')[1] elif param.startswith('show_search'): value = param.split('=')[1] if value == 'true': self.show_search = True else: self.show_search = False self.response.out.write("

Full Contact Search

") self.response.out.write("

Search all fields in your Gmail Address Book, not just name and e-mail

") if self.GetAuthentication(): #if authentication is found, run the query. If not, stop executing #output search box self.WriteSearchBox() if self.q != None: self.RunSearch() else: self.response.out.write("""

You must be signed in order to search. You also need to allow access to your contacts if that link is displayed above. Please click the sign in button above.

""") def RunSearch(self): '''Constructs and runs the query and prints the results that search term''' self.response.out.write('
') query = self.ConstructQuery() try: #self.response.out.write(self.client.GetAuthSubToken()) #debug statement feed = self.client.GetContactsFeed(query.ToUri()) #Run query and get results if feed.entry: self.PrintResults(feed.entry) else: self.response.out.write('No entries in feed.
\n') except: #error occured, probably due to bad token. To fix, clear tokens to prompt user to reauthenticate self.ClearTokens() self.response.out.write('
') def PrintResults(self,entries): '''Prints results that match the search term''' for i, entry in enumerate(entries): if self.FindQueryInEntry(self.q,entry): self.response.out.write('\n%s %s

\n' % (entry.title.text, self.FormatEntry(entry))) def PrintSignInOutLink(self): # Allow the user to sign in or sign out if self.current_user: self.response.out.write('Sign Out
' % ( users.CreateLogoutURL(self.request.uri))) else: self.response.out.write('Sign In
' % ( users.CreateLoginURL(self.request.uri))) def ConstructQuery(self): query = gdata.contacts.service.ContactsQuery() query.max_results = 1000 query.start_index = 1 query.orderby = 'lastmodified' query['sortorder'] = "descending" return query def WriteSearchBox(self): q_text = "" if self.q != None: q_text = self.q self.response.out.write("""
""" % (q_text)) def FindQueryInEntry(self, query, entry): '''Search all entry attributes for the query''' if entry.organization: if entry.organization.org_title: if self.Contains(query, entry.organization.org_title.text): return True if entry.organization.org_name: if self.Contains(query, entry.organization.org_name.text): return True if entry.phone_number: for single_phone in entry.phone_number: if self.Contains(query, str(single_phone)): return True if entry.postal_address: for single_address in entry.postal_address: if self.Contains(query,str(single_address)): return True if entry.im: for single_im in entry.im: if self.Contains(query, str(single_im.address)): return True if entry.email: for single_email in entry.email: if self.Contains(query, str(single_email.address)): return True if entry.content: if self.Contains(query, str(entry.content)): return True if self.Contains(query, str(entry.title.text)): return True return False def Contains(self, query, string_to_search): if str(string_to_search).lower().find(str(query).lower()) != -1: return True else: return False def FormatEntry(self, entry): '''Format contact attributes for display''' text = "" if entry.organization: if entry.organization.org_title: text = text + "\n
" + "Title: " + entry.organization.org_title.text if entry.organization.org_name: text = text + "\n
" + "Company: " + entry.organization.org_name.text if entry.phone_number: for single_phone in entry.phone_number: text = text + "\n
Phone Number:" + str(single_phone) if entry.postal_address: for single_address in entry.postal_address: text = text + "\n
Address:" + str(single_address) if entry.im: for single_im in entry.im: text = text + "\n
IM:" + str(single_im.address) if entry.email: for single_email in entry.email: text = text + "\n
Email:" + str(single_email.address) if entry.content: text = text + "\n
Notes:" + str(entry.content) return text def GetAuthentication(self): self.client = gdata.contacts.service.ContactsService() gdata.alt.appengine.run_on_appengine(self.client) isAuthenticated = False if self.current_user: #without the current user can't do anything if self.LookupToken(): #check if a token exsits in the database; if not, need to authenticate isAuthenticated = True else: if self.token: #check if we got a token from the query string #self.response.out.write("TOKEN FOUND...TRYING TO STORE") #Debug statement # Upgrade to a session token and store the session token. self.UpgradeAndStoreToken() isAuthenticated = True else: #if we didn't get a token, write link to authentication page authsub_next_url = ('http://%s/' % HOST_NAME) # Generate the AuthSub URL and write a page that includes the link self.response.out.write(""" Request token to allow access to Google Contacts """ % self.client.GenerateAuthSubURL(authsub_next_url, 'http://www.google.com/m8/feeds/', secure=False, session=True)) return isAuthenticated def UpgradeAndStoreToken(self): self.client.SetAuthSubToken(self.token) self.client.UpgradeToSessionToken() #self.response.out.write("TOKEN UPGRADED: " + self.client.GetAuthSubToken()) #debug statement if self.current_user: #clear old tokens self.ClearTokens() # Create a new token object for the data store which associates the # session token with the requested URL and the current user. new_token = StoredToken(user_email=self.current_user.email(), session_token=self.client.GetAuthSubToken(), target_url=self.token_scope) new_token.put() def ClearTokens(self): if self.current_user: stored_tokens = self.GetTokens() for token in stored_tokens: token.delete() def GetTokens(self): return StoredToken.gql('WHERE user_email = :1',self.current_user.email()) def LookupToken(self): if self.current_user: stored_tokens = self.GetTokens() result = False for token in stored_tokens: self.client.SetAuthSubToken(token.session_token) #self.response.out.write("FOUND TOKEN: " + self.client.GetAuthSubToken()) #debug statement result = True return result def main(): application = webapp.WSGIApplication([('/.*', Fetcher),], debug=True) wsgiref.handlers.CGIHandler().run(application) if __name__ == '__main__': main()