mirror of
https://github.com/bodyrep/bodyrep-sandpit.git
synced 2026-01-25 06:11:36 +00:00
Consolidation
Improved namespace structure, settings
This commit is contained in:
0
bodyrep/apps/core/auth/__init__.py
Normal file
0
bodyrep/apps/core/auth/__init__.py
Normal file
73
bodyrep/apps/core/auth/views.py
Normal file
73
bodyrep/apps/core/auth/views.py
Normal file
@@ -0,0 +1,73 @@
|
||||
import urllib
|
||||
import urlparse
|
||||
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.sites.models import Site
|
||||
from django.http import HttpResponseForbidden, HttpResponseRedirect
|
||||
from django.conf import settings
|
||||
from utils.functions import ajax_login_required, render_to
|
||||
|
||||
from vendor import facebook
|
||||
|
||||
@render_to('core/auth/facebook.html')
|
||||
def facebook_connect(request):
|
||||
facebook_app_id = settings.FACEBOOK_APP_ID
|
||||
facebook_secret = settings.FACEBOOK_SECRET
|
||||
|
||||
args = {
|
||||
"client_id": facebook_app_id,
|
||||
"redirect_uri": "http://" + Site.objects.get_current().domain + reverse('facebook-connect'),
|
||||
"scope": "offline_access,user_website,publish_actions",
|
||||
"display": "popup",
|
||||
}
|
||||
|
||||
verification_code = request.REQUEST.get('code')
|
||||
if verification_code:
|
||||
args["client_secret"] = facebook_secret
|
||||
args["code"] = verification_code
|
||||
uri = "https://graph.facebook.com/oauth/access_token?" + \
|
||||
urllib.urlencode(args)
|
||||
response_text = urllib.urlopen(uri).read()
|
||||
response = urlparse.parse_qs(response_text)
|
||||
|
||||
if "access_token" not in response:
|
||||
logging.user(request, "~BB~FRFailed Facebook connect")
|
||||
return dict(error="Facebook has returned an error. Try connecting again.")
|
||||
|
||||
access_token = response["access_token"][-1]
|
||||
|
||||
# Get the user's profile.
|
||||
graph = facebook.GraphAPI(access_token)
|
||||
profile = graph.get_object("me")
|
||||
uid = profile["id"]
|
||||
|
||||
# Be sure that two people aren't using the same Facebook account.
|
||||
existing_user = MSocialServices.objects.filter(facebook_uid=uid)
|
||||
if existing_user and existing_user[0].user_id != request.user.pk:
|
||||
user = User.objects.get(pk=existing_user[0].user_id)
|
||||
#logging.user(request, "~BB~FRFailed FB connect, another user: %s" % user.username)
|
||||
return dict(error=("Another user (%s, %s) has "
|
||||
"already connected with those Facebook credentials."
|
||||
% (user.username, user.email or "no email")))
|
||||
|
||||
social_services, _ = MSocialServices.objects.get_or_create(user_id=request.user.pk)
|
||||
social_services.facebook_uid = uid
|
||||
social_services.facebook_access_token = access_token
|
||||
social_services.syncing_facebook = True
|
||||
social_services.save()
|
||||
|
||||
SyncFacebookFriends.delay(user_id=request.user.pk)
|
||||
|
||||
#srequest, "~BB~FRFinishing Facebook connect")
|
||||
return {}
|
||||
elif request.REQUEST.get('error'):
|
||||
#logging.user(request, "~BB~FRFailed Facebook connect")
|
||||
return {'error': '%s... Try connecting again.' % request.REQUEST.get('error')}
|
||||
else:
|
||||
# Start the OAuth process
|
||||
#logging.user(request, "~BB~FRStarting Facebook connect")
|
||||
url = "https://www.facebook.com/dialog/oauth?" + urllib.urlencode(args)
|
||||
return {'next': url}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import datetime
|
||||
|
||||
# Django
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth import authenticate
|
||||
from django.db.models import Q
|
||||
|
||||
# Br
|
||||
from apps.core.models import Members
|
||||
|
||||
class LoginForm(forms.Form):
|
||||
@@ -13,7 +17,6 @@ class LoginForm(forms.Form):
|
||||
password = forms.CharField(label=_("Password"),
|
||||
widget=forms.PasswordInput(attrs={'tabindex': 2, 'class': 'NB-input'}),
|
||||
required=False)
|
||||
# error_messages={'required': 'Please enter a password.'})
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.user_cache = None
|
||||
@@ -35,18 +38,16 @@ class LoginForm(forms.Form):
|
||||
if self.user_cache is None:
|
||||
self.user_cache = authenticate(username=email_username[0].username, password="")
|
||||
if self.user_cache is None:
|
||||
# logging.info(" ***> [%s] Bad Login: TRYING JK-LESS PASSWORD" % username)
|
||||
jkless_password = password.replace('j', '').replace('k', '')
|
||||
self.user_cache = authenticate(username=username, password=jkless_password)
|
||||
if self.user_cache is None:
|
||||
#logging.info(" ***> [%s] Bad Login" % username)
|
||||
raise forms.ValidationError(_("Whoopsy-daisy. Try again."))
|
||||
else:
|
||||
raise forms.ValidationError(_("Login failed. Try again"))
|
||||
# else:
|
||||
# Supreme fuck-up. Accidentally removed the letters J and K from
|
||||
# all user passwords. Re-save with correct password.
|
||||
#logging.info(" ***> [%s] FIXING JK-LESS PASSWORD" % username)
|
||||
self.user_cache.set_password(password)
|
||||
self.user_cache.save()
|
||||
# self.user_cache.set_password(password)
|
||||
# self.user_cache.save()
|
||||
if not self.user_cache.is_active:
|
||||
raise forms.ValidationError(_("This account is inactive."))
|
||||
elif username and not user:
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import datetime
|
||||
import time
|
||||
import sys
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
# Django
|
||||
from django.shortcuts import render, render_to_response
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.template.loader import render_to_string
|
||||
@@ -13,20 +14,17 @@ from django.contrib.auth import logout as logout_user
|
||||
from django.contrib.auth.models import User
|
||||
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, Http404
|
||||
from django.conf import settings
|
||||
from django.core.mail import mail_admins
|
||||
from django.core.validators import email_re
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
from django.contrib.sites.models import Site
|
||||
from bodyrep.apps.core.forms import LoginForm, EditProfileForm
|
||||
from django.core.context_processors import csrf
|
||||
from django.template import RequestContext
|
||||
|
||||
# Libs
|
||||
from annoying.decorators import render_to, ajax_request
|
||||
|
||||
# BR
|
||||
from apps.core.forms import LoginForm, EditProfileForm
|
||||
from apps.core.models import Members
|
||||
|
||||
from utils.user_functions import get_user, ajax_login_required
|
||||
from utils.view_functions import get_argument_or_404, render_to, is_true
|
||||
#from utils.ratelimit import ratelimit
|
||||
from utils.functions import get_user, render_to
|
||||
|
||||
@render_to('core/landing.html')
|
||||
def index(request):
|
||||
@@ -40,9 +38,13 @@ def showUserProfile(request):
|
||||
member = Members.objects.get(username=request.user.username)
|
||||
return {'member': member}
|
||||
|
||||
@login_required
|
||||
@render_to('core/member/profile.html')
|
||||
def showMemberProfile(request, username):
|
||||
member = Members.objects.get(username=username)
|
||||
try:
|
||||
member = Members.objects.get(username=username)
|
||||
except Members.DoesNotExist:
|
||||
return render_to_response('core/errors/memberNotFound.html')
|
||||
|
||||
if request.user.is_anonymous():
|
||||
return login(request)
|
||||
@@ -53,7 +55,7 @@ def showMemberProfile(request, username):
|
||||
@render_to('core/auth/login.html')
|
||||
def login(request):
|
||||
if not request.user.is_anonymous():
|
||||
return HttpResponseRedirect(reverse('mprofile'))
|
||||
return HttpResponseRedirect(reverse('showUserProfile'))
|
||||
|
||||
if request.method == "POST":
|
||||
if request.POST.get('submit', '').startswith('log'):
|
||||
@@ -75,7 +77,7 @@ def dologin(request):
|
||||
form = LoginForm(request.POST, prefix='login')
|
||||
if form.is_valid():
|
||||
login_user(request, form.get_user())
|
||||
return HttpResponseRedirect(reverse('mprofile'))
|
||||
return HttpResponseRedirect(reverse('showUserProfile'))
|
||||
else:
|
||||
message = form.errors.items()[0][1][0]
|
||||
|
||||
@@ -92,6 +94,7 @@ def logout(request):
|
||||
@ajax_request
|
||||
@render_to('core/user/editProfile.html')
|
||||
def editUserProfile(request):
|
||||
sys.stderr.write('xxx\n\n')
|
||||
if request.user.is_anonymous():
|
||||
return login(request)
|
||||
else:
|
||||
|
||||
@@ -12,6 +12,12 @@ display: none;
|
||||
width : 170px;
|
||||
border-right : 1px solid #e6e6e6;
|
||||
}
|
||||
#banner p {
|
||||
padding: 20px 0px;
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
color: navy;
|
||||
}
|
||||
|
||||
.mc-profile-pic {
|
||||
width : 140px;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
$(function() {
|
||||
$('#edprf').unbind().live('click', function() {
|
||||
$.get('/app_dev.php/m/profile/edit', {}, function(data) {
|
||||
$.get('/m/profile/edit', {}, function(data) {
|
||||
$('#mcnt').html(data);
|
||||
});
|
||||
});
|
||||
@@ -19,7 +19,7 @@ $(function() {
|
||||
$.post(frm.attr('action'), frm.serialize(), function(response) {
|
||||
|
||||
if(response.result)
|
||||
$('#mcnt').append('<b>Updated</b>');
|
||||
$('#banner').html('<p>Profile Updated</p>');
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
@@ -60,6 +60,9 @@ PASSWORD_HASHERS = (
|
||||
'django.contrib.auth.hashers.BCryptPasswordHasher',
|
||||
)
|
||||
|
||||
FACEBOOK_APP_ID = ''
|
||||
FACEBOOK_SECRET = ''
|
||||
|
||||
ADMIN_MEDIA_PREFIX = '/static/admin/'
|
||||
|
||||
# Additional locations of static files
|
||||
|
||||
7
bodyrep/templates/core/errors/memberNotFound.html
Normal file
7
bodyrep/templates/core/errors/memberNotFound.html
Normal file
@@ -0,0 +1,7 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
Member not found
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,9 +1,9 @@
|
||||
{% block content %}
|
||||
|
||||
<h1>Update Profile</h1>
|
||||
<div id='banner'></div>
|
||||
|
||||
|
||||
<form action="{% url msave %}" method="post" id="profile">
|
||||
<form action="{% url saveUserProfile %}" method="post" id="profile">
|
||||
<div>
|
||||
{% if edit_form.errors %}
|
||||
{% for field, error in edit_form.errors.items %}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<span class="caret" style='border-top-color : white; border-bottom-color : white;'></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{% url mprofile %}">Profile</a></li>
|
||||
<li><a href="{% url showUserProfile %}">Profile</a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a href="{% url logout %}">Sign Out</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -1,33 +1,38 @@
|
||||
# Django libs
|
||||
from django.conf.urls.defaults import patterns, include, url
|
||||
from apps.core import views as core_views
|
||||
from django.conf import settings
|
||||
from django.contrib import admin
|
||||
|
||||
# BR libs
|
||||
from apps.core import views as coreviews
|
||||
from apps.core.auth import views as authviews
|
||||
|
||||
admin.autodiscover()
|
||||
|
||||
urlpatterns = patterns('',
|
||||
# Index
|
||||
url(r'^$', core_views.index, name='index'),
|
||||
url(r'^$', coreviews.index, name='index'),
|
||||
|
||||
# Static
|
||||
(r'^s/(?P<path>.*)$', 'django.views.static.serve',
|
||||
{'document_root': settings.STATIC_ROOT}),
|
||||
|
||||
# User tasks
|
||||
url(r'^m/profile/?$', core_views.showUserProfile, name='mprofile'),
|
||||
url(r'^m/profile/save/?$', core_views.saveUserProfile, name='msave'),
|
||||
url(r'^m/profile/edit/?$', core_views.editUserProfile, name='medit'),
|
||||
url(r'^m/profile/?$', coreviews.showUserProfile, name='showUserProfile'),
|
||||
url(r'^m/profile/save/?$', coreviews.saveUserProfile, name='saveUserProfile'),
|
||||
url(r'^m/profile/edit/?$', coreviews.editUserProfile, name='editUser'),
|
||||
|
||||
# Auth
|
||||
url(r'^logout$', core_views.logout, name='logout'),
|
||||
url(r'^login$', core_views.login, name='login'),
|
||||
url(r'^dologin$', core_views.dologin, name='dologin'),
|
||||
url(r'^logout$', coreviews.logout, name='logout'),
|
||||
url(r'^login$', coreviews.login, name='login'),
|
||||
url(r'^dologin$', coreviews.dologin, name='dologin'),
|
||||
url(r'^facebook_connect/?$', authviews.facebook_connect, name='facebook-connect'),
|
||||
|
||||
# Django
|
||||
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
||||
url(r'^admin/', include(admin.site.urls)),
|
||||
|
||||
# Member profile
|
||||
url(r'^([a-zA-Z0-9]+)', core_views.showMemberProfile),
|
||||
url(r'^([a-zA-Z0-9]+)', coreviews.showMemberProfile),
|
||||
|
||||
)
|
||||
|
||||
@@ -5,6 +5,16 @@ from django.utils.hashcompat import md5_constructor
|
||||
from django.utils.http import urlquote
|
||||
from django.http import HttpResponseForbidden
|
||||
from django.conf import settings
|
||||
from django.template import RequestContext
|
||||
from django.shortcuts import render_to_response
|
||||
|
||||
# BR
|
||||
|
||||
|
||||
|
||||
#
|
||||
# NewsBlur functions https://github.com/samuelclay/NewsBlur
|
||||
#
|
||||
|
||||
def ajax_login_required(function=None):
|
||||
def _dec(view_func):
|
||||
@@ -25,24 +35,6 @@ def ajax_login_required(function=None):
|
||||
else:
|
||||
return _dec(function)
|
||||
|
||||
def admin_only(function=None):
|
||||
def _dec(view_func):
|
||||
def _view(request, *args, **kwargs):
|
||||
if not request.user.is_staff:
|
||||
return HttpResponseForbidden()
|
||||
else:
|
||||
return view_func(request, *args, **kwargs)
|
||||
|
||||
_view.__name__ = view_func.__name__
|
||||
_view.__dict__ = view_func.__dict__
|
||||
_view.__doc__ = view_func.__doc__
|
||||
|
||||
return _view
|
||||
|
||||
if function is None:
|
||||
return _dec
|
||||
else:
|
||||
return _dec(function)
|
||||
|
||||
def get_user(request):
|
||||
if not hasattr(request, 'user'):
|
||||
@@ -106,4 +98,34 @@ def extract_user_agent(request):
|
||||
elif 'WP8' in user_agent:
|
||||
platform = 'WP8'
|
||||
|
||||
return platform
|
||||
return platform
|
||||
|
||||
def render_to(template):
|
||||
"""
|
||||
Decorator for Django views that sends returned dict to render_to_response function
|
||||
with given template and RequestContext as context instance.
|
||||
|
||||
If view doesn't return dict then decorator simply returns output.
|
||||
Additionally view can return two-tuple, which must contain dict as first
|
||||
element and string with template name as second. This string will
|
||||
override template name, given as parameter
|
||||
|
||||
Parameters:
|
||||
|
||||
- template: template name to use
|
||||
"""
|
||||
def renderer(func):
|
||||
def wrapper(request, *args, **kw):
|
||||
output = func(request, *args, **kw)
|
||||
if isinstance(output, (list, tuple)):
|
||||
return render_to_response(output[1], output[0], RequestContext(request))
|
||||
elif isinstance(output, dict):
|
||||
return render_to_response(template, output, RequestContext(request))
|
||||
return output
|
||||
return wrapper
|
||||
return renderer
|
||||
|
||||
def is_true(value):
|
||||
if value == 1:
|
||||
return True
|
||||
return bool(value) and isinstance(value, basestring) and value.lower() not in ('false', '0')
|
||||
@@ -1,39 +0,0 @@
|
||||
from django.http import Http404
|
||||
from django.template import RequestContext
|
||||
from django.shortcuts import render_to_response
|
||||
|
||||
def get_argument_or_404(request, param, method='REQUEST'):
|
||||
try:
|
||||
return getattr(request, method)[param]
|
||||
except KeyError:
|
||||
raise Http404
|
||||
|
||||
def render_to(template):
|
||||
"""
|
||||
Decorator for Django views that sends returned dict to render_to_response function
|
||||
with given template and RequestContext as context instance.
|
||||
|
||||
If view doesn't return dict then decorator simply returns output.
|
||||
Additionally view can return two-tuple, which must contain dict as first
|
||||
element and string with template name as second. This string will
|
||||
override template name, given as parameter
|
||||
|
||||
Parameters:
|
||||
|
||||
- template: template name to use
|
||||
"""
|
||||
def renderer(func):
|
||||
def wrapper(request, *args, **kw):
|
||||
output = func(request, *args, **kw)
|
||||
if isinstance(output, (list, tuple)):
|
||||
return render_to_response(output[1], output[0], RequestContext(request))
|
||||
elif isinstance(output, dict):
|
||||
return render_to_response(template, output, RequestContext(request))
|
||||
return output
|
||||
return wrapper
|
||||
return renderer
|
||||
|
||||
def is_true(value):
|
||||
if value == 1:
|
||||
return True
|
||||
return bool(value) and isinstance(value, basestring) and value.lower() not in ('false', '0')
|
||||
0
bodyrep/vendor/__init__.py
vendored
Normal file
0
bodyrep/vendor/__init__.py
vendored
Normal file
457
bodyrep/vendor/facebook.py
vendored
Normal file
457
bodyrep/vendor/facebook.py
vendored
Normal file
@@ -0,0 +1,457 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2010 Facebook
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Python client library for the Facebook Platform.
|
||||
|
||||
This client library is designed to support the Graph API and the official
|
||||
Facebook JavaScript SDK, which is the canonical way to implement
|
||||
Facebook authentication. Read more about the Graph API at
|
||||
http://developers.facebook.com/docs/api. You can download the Facebook
|
||||
JavaScript SDK at http://github.com/facebook/connect-js/.
|
||||
|
||||
If your application is using Google AppEngine's webapp framework, your
|
||||
usage of this module might look like this:
|
||||
|
||||
user = facebook.get_user_from_cookie(self.request.cookies, key, secret)
|
||||
if user:
|
||||
graph = facebook.GraphAPI(user["access_token"])
|
||||
profile = graph.get_object("me")
|
||||
friends = graph.get_connections("me", "friends")
|
||||
|
||||
"""
|
||||
|
||||
import cgi
|
||||
import time
|
||||
import urllib
|
||||
import urllib2
|
||||
import hashlib
|
||||
import hmac
|
||||
import base64
|
||||
import logging
|
||||
|
||||
# Find a JSON parser
|
||||
try:
|
||||
import simplejson as json
|
||||
except ImportError:
|
||||
try:
|
||||
from django.utils import simplejson as json
|
||||
except ImportError:
|
||||
import json
|
||||
_parse_json = json.loads
|
||||
|
||||
# Find a query string parser
|
||||
try:
|
||||
from urlparse import parse_qs
|
||||
except ImportError:
|
||||
from cgi import parse_qs
|
||||
|
||||
class GraphAPI(object):
|
||||
"""A client for the Facebook Graph API.
|
||||
|
||||
See http://developers.facebook.com/docs/api for complete documentation
|
||||
for the API.
|
||||
|
||||
The Graph API is made up of the objects in Facebook (e.g., people, pages,
|
||||
events, photos) and the connections between them (e.g., friends,
|
||||
photo tags, and event RSVPs). This client provides access to those
|
||||
primitive types in a generic way. For example, given an OAuth access
|
||||
token, this will fetch the profile of the active user and the list
|
||||
of the user's friends:
|
||||
|
||||
graph = facebook.GraphAPI(access_token)
|
||||
user = graph.get_object("me")
|
||||
friends = graph.get_connections(user["id"], "friends")
|
||||
|
||||
You can see a list of all of the objects and connections supported
|
||||
by the API at http://developers.facebook.com/docs/reference/api/.
|
||||
|
||||
You can obtain an access token via OAuth or by using the Facebook
|
||||
JavaScript SDK. See http://developers.facebook.com/docs/authentication/
|
||||
for details.
|
||||
|
||||
If you are using the JavaScript SDK, you can use the
|
||||
get_user_from_cookie() method below to get the OAuth access token
|
||||
for the active user from the cookie saved by the SDK.
|
||||
"""
|
||||
def __init__(self, access_token=None):
|
||||
self.access_token = access_token
|
||||
|
||||
def get_object(self, id, **args):
|
||||
"""Fetchs the given object from the graph."""
|
||||
return self.request(id, args)
|
||||
|
||||
def get_objects(self, ids, **args):
|
||||
"""Fetchs all of the given object from the graph.
|
||||
|
||||
We return a map from ID to object. If any of the IDs are invalid,
|
||||
we raise an exception.
|
||||
"""
|
||||
args["ids"] = ",".join(ids)
|
||||
return self.request("", args)
|
||||
|
||||
def get_connections(self, id, connection_name, **args):
|
||||
"""Fetchs the connections for given object."""
|
||||
return self.request(id + "/" + connection_name, args)
|
||||
|
||||
def put_object(self, parent_object, connection_name, **data):
|
||||
"""Writes the given object to the graph, connected to the given parent.
|
||||
|
||||
For example,
|
||||
|
||||
graph.put_object("me", "feed", message="Hello, world")
|
||||
|
||||
writes "Hello, world" to the active user's wall. Likewise, this
|
||||
will comment on a the first post of the active user's feed:
|
||||
|
||||
feed = graph.get_connections("me", "feed")
|
||||
post = feed["data"][0]
|
||||
graph.put_object(post["id"], "comments", message="First!")
|
||||
|
||||
See http://developers.facebook.com/docs/api#publishing for all of
|
||||
the supported writeable objects.
|
||||
|
||||
Most write operations require extended permissions. For example,
|
||||
publishing wall posts requires the "publish_stream" permission. See
|
||||
http://developers.facebook.com/docs/authentication/ for details about
|
||||
extended permissions.
|
||||
"""
|
||||
assert self.access_token, "Write operations require an access token"
|
||||
return self.request(parent_object + "/" + connection_name, post_args=data)
|
||||
|
||||
def put_wall_post(self, message, attachment={}, profile_id="me"):
|
||||
"""Writes a wall post to the given profile's wall.
|
||||
|
||||
We default to writing to the authenticated user's wall if no
|
||||
profile_id is specified.
|
||||
|
||||
attachment adds a structured attachment to the status message being
|
||||
posted to the Wall. It should be a dictionary of the form:
|
||||
|
||||
{"name": "Link name"
|
||||
"link": "http://www.example.com/",
|
||||
"caption": "{*actor*} posted a new review",
|
||||
"description": "This is a longer description of the attachment",
|
||||
"picture": "http://www.example.com/thumbnail.jpg"}
|
||||
|
||||
"""
|
||||
return self.put_object(profile_id, "feed", message=message, **attachment)
|
||||
|
||||
def put_comment(self, object_id, message):
|
||||
"""Writes the given comment on the given post."""
|
||||
return self.put_object(object_id, "comments", message=message)
|
||||
|
||||
def put_like(self, object_id):
|
||||
"""Likes the given post."""
|
||||
return self.put_object(object_id, "likes")
|
||||
|
||||
def delete_object(self, id):
|
||||
"""Deletes the object with the given ID from the graph."""
|
||||
self.request(id, post_args={"method": "delete"})
|
||||
|
||||
def put_photo(self, image, message=None, album_id=None, **kwargs):
|
||||
"""Uploads an image using multipart/form-data
|
||||
image=File like object for the image
|
||||
message=Caption for your image
|
||||
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
|
||||
post_args = {
|
||||
'access_token': self.access_token,
|
||||
'source': image,
|
||||
'message': message
|
||||
}
|
||||
post_args.update(kwargs)
|
||||
content_type, body = self._encode_multipart_form(post_args)
|
||||
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()
|
||||
#For Python 3 use this:
|
||||
#except urllib2.HTTPError as e:
|
||||
except urllib2.HTTPError, e:
|
||||
data = e.read() # Facebook sends OAuth errors as 400, and urllib2 throws an exception, we want a GraphAPIError
|
||||
try:
|
||||
response = _parse_json(data)
|
||||
# Raise an error if we got one, but don't freak out if Facebook just gave us a Bool value
|
||||
if response and isinstance(response, dict) and 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():
|
||||
logging.debug("Encoding %s, (%s)%s" % (key, type(value), value))
|
||||
if not value:
|
||||
continue
|
||||
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()
|
||||
logging.debug(type(value))
|
||||
else:
|
||||
L.append('Content-Disposition: form-data; name="%s"' % key)
|
||||
L.append('')
|
||||
if isinstance(value, unicode):
|
||||
logging.debug("Convert to ascii")
|
||||
value = value.encode('ascii')
|
||||
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.
|
||||
|
||||
We translate args to a valid query string. If post_args is given,
|
||||
we send a POST request to the given path with the given arguments.
|
||||
"""
|
||||
if not args: args = {}
|
||||
if self.access_token:
|
||||
if post_args is not None:
|
||||
post_args["access_token"] = self.access_token
|
||||
else:
|
||||
args["access_token"] = self.access_token
|
||||
post_data = None if post_args is None else urllib.urlencode(post_args)
|
||||
try:
|
||||
file = urllib2.urlopen("https://graph.facebook.com/" + path + "?" +
|
||||
urllib.urlencode(args), post_data)
|
||||
except urllib2.HTTPError, e:
|
||||
response = _parse_json( e.read() )
|
||||
raise GraphAPIError(response["error"]["type"],
|
||||
response["error"]["message"])
|
||||
|
||||
try:
|
||||
fileInfo = file.info()
|
||||
if fileInfo.maintype == 'text':
|
||||
response = _parse_json(file.read())
|
||||
elif fileInfo.maintype == 'image':
|
||||
mimetype = fileInfo['content-type']
|
||||
response = {
|
||||
"data": file.read(),
|
||||
"mime-type": mimetype,
|
||||
"url": file.url,
|
||||
}
|
||||
else:
|
||||
raise GraphAPIError('Response Error', 'Maintype was not text or image')
|
||||
finally:
|
||||
file.close()
|
||||
if response and isinstance(response, dict) and response.get("error"):
|
||||
raise GraphAPIError(response["error"]["type"],
|
||||
response["error"]["message"])
|
||||
return response
|
||||
|
||||
def api_request(self, path, args=None, post_args=None):
|
||||
"""Fetches the given path in the Graph API.
|
||||
|
||||
We translate args to a valid query string. If post_args is given,
|
||||
we send a POST request to the given path with the given arguments.
|
||||
"""
|
||||
if not args: args = {}
|
||||
if self.access_token:
|
||||
if post_args is not None:
|
||||
post_args["access_token"] = self.access_token
|
||||
else:
|
||||
args["access_token"] = self.access_token
|
||||
if self.api_key:
|
||||
if post_args is not None:
|
||||
post_args["api_key"] = self.api_key
|
||||
else:
|
||||
args["api_key"] = self.api_key
|
||||
if post_args is not None:
|
||||
post_args["format"] = "json-strings"
|
||||
else:
|
||||
args["format"] = "json-strings"
|
||||
post_data = None if post_args is None else urllib.urlencode(post_args)
|
||||
file = urllib.urlopen("https://api.facebook.com/method/" + path + "?" +
|
||||
urllib.urlencode(args), post_data)
|
||||
try:
|
||||
response = _parse_json(file.read())
|
||||
finally:
|
||||
file.close()
|
||||
if response and response.get("error"):
|
||||
raise GraphAPIError(response["error"]["type"],
|
||||
response["error"]["message"])
|
||||
return response
|
||||
|
||||
|
||||
def fql(self, query, args=None, post_args=None):
|
||||
"""FQL query.
|
||||
Two reasons to have this method:
|
||||
1. Graph api does not expose some info fields of a user, e.g.
|
||||
a user's networks/affiliations, we have to fall back to old api.
|
||||
2. FQL is a strong tool.
|
||||
Example query: "SELECT affiliations FROM user WHERE uid = me()"
|
||||
"""
|
||||
if not args: args = {}
|
||||
if self.access_token:
|
||||
if post_args is not None:
|
||||
post_args["access_token"] = self.access_token
|
||||
else:
|
||||
args["access_token"] = self.access_token
|
||||
post_data = None if post_args is None else urllib.urlencode(post_args)
|
||||
|
||||
args["query"] = query
|
||||
args["format"]="json"
|
||||
file = urllib2.urlopen("https://api.facebook.com/method/fql.query?" +
|
||||
urllib.urlencode(args), post_data)
|
||||
try:
|
||||
content = file.read()
|
||||
response = _parse_json(content)
|
||||
#Return a list if success, return a dictionary if failed
|
||||
if type(response) is dict and "error_code" in response:
|
||||
raise GraphAPIError(response["error_code"],response["error_msg"])
|
||||
except Exception, e:
|
||||
raise e
|
||||
finally:
|
||||
file.close()
|
||||
|
||||
return response
|
||||
|
||||
|
||||
class GraphAPIError(Exception):
|
||||
def __init__(self, type, message):
|
||||
Exception.__init__(self, message)
|
||||
self.type = type
|
||||
|
||||
def get_user_from_cookie(cookies, app_id, app_secret):
|
||||
"""Parses the cookie set by the official Facebook JavaScript SDK.
|
||||
|
||||
cookies should be a dictionary-like object mapping cookie names to
|
||||
cookie values.
|
||||
|
||||
If the user is logged in via Facebook, we return a dictionary with the
|
||||
keys "uid" and "access_token". The former is the user's Facebook ID,
|
||||
and the latter can be used to make authenticated requests to the Graph API.
|
||||
If the user is not logged in, we return None.
|
||||
|
||||
Download the official Facebook JavaScript SDK at
|
||||
http://github.com/facebook/connect-js/. Read more about Facebook
|
||||
authentication at http://developers.facebook.com/docs/authentication/.
|
||||
"""
|
||||
cookie = cookies.get("fbsr_" + app_id, "")
|
||||
if not cookie: return None
|
||||
parsed_request = parse_signed_request(cookie, app_secret)
|
||||
result = get_access_token_from_code(parsed_request["code"], "",
|
||||
app_id, app_secret)
|
||||
result["uid"] = parsed_request["user_id"]
|
||||
return result
|
||||
|
||||
def parse_signed_request(signed_request, app_secret):
|
||||
""" Return dictionary with signed request data.
|
||||
|
||||
We return a dictionary containing the information in the signed_request. This will
|
||||
include a user_id if the user has authorised your application, as well as any
|
||||
information requested in the scope.
|
||||
|
||||
If the signed_request is malformed or corrupted, False is returned.
|
||||
"""
|
||||
try:
|
||||
l = signed_request.split('.', 2)
|
||||
encoded_sig = str(l[0])
|
||||
payload = str(l[1])
|
||||
sig = base64.urlsafe_b64decode(encoded_sig + "=" * ((4 - len(encoded_sig) % 4) % 4))
|
||||
data = base64.urlsafe_b64decode(payload + "=" * ((4 - len(payload) % 4) % 4))
|
||||
except IndexError:
|
||||
return False # raise ValueError('signed_request malformed')
|
||||
except TypeError:
|
||||
return False # raise ValueError('signed_request had corrupted payload')
|
||||
|
||||
data = _parse_json(data)
|
||||
if data.get('algorithm', '').upper() != 'HMAC-SHA256':
|
||||
return False # raise ValueError('signed_request used unknown algorithm')
|
||||
|
||||
expected_sig = hmac.new(app_secret, msg=payload, digestmod=hashlib.sha256).digest()
|
||||
if sig != expected_sig:
|
||||
return False # raise ValueError('signed_request had signature mismatch')
|
||||
|
||||
return data
|
||||
|
||||
def auth_url(app_id, canvas_url, perms = None):
|
||||
url = "https://www.facebook.com/dialog/oauth?"
|
||||
kvps = {'client_id': app_id, 'redirect_uri': canvas_url}
|
||||
if perms:
|
||||
kvps['scope'] = ",".join(perms)
|
||||
return url + urllib.urlencode(kvps)
|
||||
|
||||
|
||||
def get_access_token_from_code(code, redirect_uri, app_id, app_secret):
|
||||
"""
|
||||
Get a user-specific access token from the "code" returned from a Facebook
|
||||
OAuth dialog. Returns a dict containing the access token and its expiration
|
||||
date (if applicable).
|
||||
|
||||
"""
|
||||
args = {
|
||||
"code": code,
|
||||
"redirect_uri": redirect_uri,
|
||||
"client_id": app_id,
|
||||
"client_secret": app_secret,
|
||||
}
|
||||
# We would use GraphAPI.request() here, except for that the fact that the
|
||||
# response is a key-value pair, and not JSON.
|
||||
response = urllib.urlopen("https://graph.facebook.com/oauth/access_token" +
|
||||
"?" + urllib.urlencode(args)).read()
|
||||
query_str = parse_qs(response)
|
||||
if "access_token" in query_str:
|
||||
result = {"access_token":query_str["access_token"][0]}
|
||||
if "expires" in query_str:
|
||||
result["expires"] = query_str["expires"][0]
|
||||
return result
|
||||
else:
|
||||
response = json.loads(response)
|
||||
raise GraphAPIError(response["error"]["type"],
|
||||
response["error"]["message"])
|
||||
|
||||
|
||||
def get_app_access_token(app_id, app_secret):
|
||||
"""
|
||||
Get the access_token for the app that can be used for insights and creating test users
|
||||
app_id = retrieved from the developer page
|
||||
app_secret = retrieved from the developer page
|
||||
returns the application access_token
|
||||
"""
|
||||
# Get an app access token
|
||||
args = {'grant_type':'client_credentials',
|
||||
'client_id':app_id,
|
||||
'client_secret':app_secret}
|
||||
|
||||
file = urllib2.urlopen("https://graph.facebook.com/oauth/access_token?" +
|
||||
urllib.urlencode(args))
|
||||
|
||||
try:
|
||||
result = file.read().split("=")[1]
|
||||
finally:
|
||||
file.close()
|
||||
|
||||
return result
|
||||
Reference in New Issue
Block a user