Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ Basic usage:
friends = graph.get_connections("me", "friends")
graph.put_object("me", "feed", message="I am writing on my wall!")

Photo uploads:

graph = facebook.GraphAPI(oauth_access_token)
tags = json.dumps([{'x':50, 'y':50, tag_uid:12345}, {'x':10, 'y':60, tag_text:'a turtle'}])
graph.put_photo(album_id_or_None, source=open('img.jpg'), message="Cool photo!", tags=tags)

If you are using the module within a web application with the
[JavaScript SDK](http://github.com/facebook/connect-js), you can also use the
module to use Facebook for login, parsing the cookie set by the JavaScript SDK
Expand All @@ -26,4 +32,5 @@ profile of the logged in user with:
profile = graph.get_object("me")
friends = graph.get_connections("me", "friends")


You can see a full AppEngine example application in examples/appengine.
80 changes: 79 additions & 1 deletion src/facebook.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
import cgi
import hashlib
import time
import urllib
import urllib, urllib2

# Find a JSON parser
try:
Expand Down Expand Up @@ -152,6 +152,84 @@ def delete_object(self, id):
"""Deletes the object with the given ID from the graph."""
self.request(id, post_args={"method": "delete"})

def put_album(self, object_id, **kwargs):
"""creates an album under the specified object (user, page etc)
object_id=None posts to /me/albums.
returns a dictionary that contains the album id
"""

object_id = object_id or "me"
assert self.access_token, "Write operations require an access token"
kwargs['access_token'] = self.access_token
content_type, body = self._encode_multipart_form(kwargs)
req = urllib2.Request("https://graph.facebook.com/%s/albums" % object_id, data=body)
req.add_header('Content-Type', content_type)
try:
data = urllib2.urlopen(req).read()
except urllib2.HTTPError as e:
data = e.read() # Facebook sends OAuth errors as 400, and urllib2 throws an exception, we want a GraphAPIError
try:
response = _parse_json(data)
if response.get("error"):
raise GraphAPIError(response["error"].get("code", 1),
response["error"]["message"])
except ValueError:
response = data

return response

def put_photo(self, album_id=None, **kwargs):
"""Uploads an image using multipart/form-data
album_id=None posts to /me/photos which uses or creates and uses
an album for your application.
"""
object_id = album_id or "me"
#it would have been nice to reuse self.request; but multipart is messy in urllib
kwargs['access_token'] = self.access_token
content_type, body = self._encode_multipart_form(kwargs)
req = urllib2.Request("https://graph.facebook.com/%s/photos" % object_id, data=body)
req.add_header('Content-Type', content_type)
try:
data = urllib2.urlopen(req).read()
except urllib2.HTTPError as e:
data = e.read() # Facebook sends OAuth errors as 400, and urllib2 throws an exception, we want a GraphAPIError
try:
response = _parse_json(data)
if response.get("error"):
raise GraphAPIError(response["error"].get("code", 1),
response["error"]["message"])
except ValueError:
response = data

return response

# based on: http://code.activestate.com/recipes/146306/
def _encode_multipart_form(self, fields):
"""Fields are a dict of form name-> value
For files, value should be a file object.
Other file-like objects might work and a fake name will be chosen.
Return (content_type, body) ready for httplib.HTTP instance
"""
BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$'
CRLF = '\r\n'
L = []
for (key, value) in fields.items():
L.append('--' + BOUNDARY)
if hasattr(value, 'read') and callable(value.read):
filename = getattr(value,'name','%s.jpg' % key)
L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename))
L.append('Content-Type: image/jpeg')
value = value.read()
else:
L.append('Content-Disposition: form-data; name="%s"' % key)
L.append('')
L.append(value)
L.append('--' + BOUNDARY + '--')
L.append('')
body = CRLF.join(L)
content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
return content_type, body

def request(self, path, args=None, post_args=None):
"""Fetches the given path in the Graph API.

Expand Down