mirror of
https://github.com/bodyrep/bodyrep-sandpit.git
synced 2026-01-25 14:21:40 +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
|
import datetime
|
||||||
|
|
||||||
|
# Django
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.auth import authenticate
|
from django.contrib.auth import authenticate
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
|
||||||
|
# Br
|
||||||
from apps.core.models import Members
|
from apps.core.models import Members
|
||||||
|
|
||||||
class LoginForm(forms.Form):
|
class LoginForm(forms.Form):
|
||||||
@@ -13,7 +17,6 @@ class LoginForm(forms.Form):
|
|||||||
password = forms.CharField(label=_("Password"),
|
password = forms.CharField(label=_("Password"),
|
||||||
widget=forms.PasswordInput(attrs={'tabindex': 2, 'class': 'NB-input'}),
|
widget=forms.PasswordInput(attrs={'tabindex': 2, 'class': 'NB-input'}),
|
||||||
required=False)
|
required=False)
|
||||||
# error_messages={'required': 'Please enter a password.'})
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.user_cache = None
|
self.user_cache = None
|
||||||
@@ -35,18 +38,16 @@ class LoginForm(forms.Form):
|
|||||||
if self.user_cache is None:
|
if self.user_cache is None:
|
||||||
self.user_cache = authenticate(username=email_username[0].username, password="")
|
self.user_cache = authenticate(username=email_username[0].username, password="")
|
||||||
if self.user_cache is None:
|
if self.user_cache is None:
|
||||||
# logging.info(" ***> [%s] Bad Login: TRYING JK-LESS PASSWORD" % username)
|
|
||||||
jkless_password = password.replace('j', '').replace('k', '')
|
jkless_password = password.replace('j', '').replace('k', '')
|
||||||
self.user_cache = authenticate(username=username, password=jkless_password)
|
self.user_cache = authenticate(username=username, password=jkless_password)
|
||||||
if self.user_cache is None:
|
if self.user_cache is None:
|
||||||
#logging.info(" ***> [%s] Bad Login" % username)
|
raise forms.ValidationError(_("Login failed. Try again"))
|
||||||
raise forms.ValidationError(_("Whoopsy-daisy. Try again."))
|
# else:
|
||||||
else:
|
|
||||||
# Supreme fuck-up. Accidentally removed the letters J and K from
|
# Supreme fuck-up. Accidentally removed the letters J and K from
|
||||||
# all user passwords. Re-save with correct password.
|
# all user passwords. Re-save with correct password.
|
||||||
#logging.info(" ***> [%s] FIXING JK-LESS PASSWORD" % username)
|
#logging.info(" ***> [%s] FIXING JK-LESS PASSWORD" % username)
|
||||||
self.user_cache.set_password(password)
|
# self.user_cache.set_password(password)
|
||||||
self.user_cache.save()
|
# self.user_cache.save()
|
||||||
if not self.user_cache.is_active:
|
if not self.user_cache.is_active:
|
||||||
raise forms.ValidationError(_("This account is inactive."))
|
raise forms.ValidationError(_("This account is inactive."))
|
||||||
elif username and not user:
|
elif username and not user:
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import time
|
import time
|
||||||
import sys
|
import sys
|
||||||
from django.shortcuts import get_object_or_404
|
|
||||||
|
# Django
|
||||||
from django.shortcuts import render, render_to_response
|
from django.shortcuts import render, render_to_response
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.template.loader import render_to_string
|
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.contrib.auth.models import User
|
||||||
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, Http404
|
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, Http404
|
||||||
from django.conf import settings
|
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 django.contrib.sites.models import Site
|
||||||
from bodyrep.apps.core.forms import LoginForm, EditProfileForm
|
|
||||||
from django.core.context_processors import csrf
|
from django.core.context_processors import csrf
|
||||||
from django.template import RequestContext
|
from django.template import RequestContext
|
||||||
|
|
||||||
|
# Libs
|
||||||
from annoying.decorators import render_to, ajax_request
|
from annoying.decorators import render_to, ajax_request
|
||||||
|
|
||||||
|
# BR
|
||||||
|
from apps.core.forms import LoginForm, EditProfileForm
|
||||||
from apps.core.models import Members
|
from apps.core.models import Members
|
||||||
|
from utils.functions import get_user, render_to
|
||||||
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
|
|
||||||
|
|
||||||
@render_to('core/landing.html')
|
@render_to('core/landing.html')
|
||||||
def index(request):
|
def index(request):
|
||||||
@@ -40,9 +38,13 @@ def showUserProfile(request):
|
|||||||
member = Members.objects.get(username=request.user.username)
|
member = Members.objects.get(username=request.user.username)
|
||||||
return {'member': member}
|
return {'member': member}
|
||||||
|
|
||||||
|
@login_required
|
||||||
@render_to('core/member/profile.html')
|
@render_to('core/member/profile.html')
|
||||||
def showMemberProfile(request, username):
|
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():
|
if request.user.is_anonymous():
|
||||||
return login(request)
|
return login(request)
|
||||||
@@ -53,7 +55,7 @@ def showMemberProfile(request, username):
|
|||||||
@render_to('core/auth/login.html')
|
@render_to('core/auth/login.html')
|
||||||
def login(request):
|
def login(request):
|
||||||
if not request.user.is_anonymous():
|
if not request.user.is_anonymous():
|
||||||
return HttpResponseRedirect(reverse('mprofile'))
|
return HttpResponseRedirect(reverse('showUserProfile'))
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
if request.POST.get('submit', '').startswith('log'):
|
if request.POST.get('submit', '').startswith('log'):
|
||||||
@@ -75,7 +77,7 @@ def dologin(request):
|
|||||||
form = LoginForm(request.POST, prefix='login')
|
form = LoginForm(request.POST, prefix='login')
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
login_user(request, form.get_user())
|
login_user(request, form.get_user())
|
||||||
return HttpResponseRedirect(reverse('mprofile'))
|
return HttpResponseRedirect(reverse('showUserProfile'))
|
||||||
else:
|
else:
|
||||||
message = form.errors.items()[0][1][0]
|
message = form.errors.items()[0][1][0]
|
||||||
|
|
||||||
@@ -92,6 +94,7 @@ def logout(request):
|
|||||||
@ajax_request
|
@ajax_request
|
||||||
@render_to('core/user/editProfile.html')
|
@render_to('core/user/editProfile.html')
|
||||||
def editUserProfile(request):
|
def editUserProfile(request):
|
||||||
|
sys.stderr.write('xxx\n\n')
|
||||||
if request.user.is_anonymous():
|
if request.user.is_anonymous():
|
||||||
return login(request)
|
return login(request)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ display: none;
|
|||||||
width : 170px;
|
width : 170px;
|
||||||
border-right : 1px solid #e6e6e6;
|
border-right : 1px solid #e6e6e6;
|
||||||
}
|
}
|
||||||
|
#banner p {
|
||||||
|
padding: 20px 0px;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: navy;
|
||||||
|
}
|
||||||
|
|
||||||
.mc-profile-pic {
|
.mc-profile-pic {
|
||||||
width : 140px;
|
width : 140px;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
$('#edprf').unbind().live('click', function() {
|
$('#edprf').unbind().live('click', function() {
|
||||||
$.get('/app_dev.php/m/profile/edit', {}, function(data) {
|
$.get('/m/profile/edit', {}, function(data) {
|
||||||
$('#mcnt').html(data);
|
$('#mcnt').html(data);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -19,7 +19,7 @@ $(function() {
|
|||||||
$.post(frm.attr('action'), frm.serialize(), function(response) {
|
$.post(frm.attr('action'), frm.serialize(), function(response) {
|
||||||
|
|
||||||
if(response.result)
|
if(response.result)
|
||||||
$('#mcnt').append('<b>Updated</b>');
|
$('#banner').html('<p>Profile Updated</p>');
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -60,6 +60,9 @@ PASSWORD_HASHERS = (
|
|||||||
'django.contrib.auth.hashers.BCryptPasswordHasher',
|
'django.contrib.auth.hashers.BCryptPasswordHasher',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
FACEBOOK_APP_ID = ''
|
||||||
|
FACEBOOK_SECRET = ''
|
||||||
|
|
||||||
ADMIN_MEDIA_PREFIX = '/static/admin/'
|
ADMIN_MEDIA_PREFIX = '/static/admin/'
|
||||||
|
|
||||||
# Additional locations of static files
|
# 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 %}
|
{% block content %}
|
||||||
|
|
||||||
<h1>Update Profile</h1>
|
<h1>Update Profile</h1>
|
||||||
|
<div id='banner'></div>
|
||||||
|
|
||||||
|
<form action="{% url saveUserProfile %}" method="post" id="profile">
|
||||||
<form action="{% url msave %}" method="post" id="profile">
|
|
||||||
<div>
|
<div>
|
||||||
{% if edit_form.errors %}
|
{% if edit_form.errors %}
|
||||||
{% for field, error in edit_form.errors.items %}
|
{% 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>
|
<span class="caret" style='border-top-color : white; border-bottom-color : white;'></span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu">
|
<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 class="divider"></li>
|
||||||
<li><a href="{% url logout %}">Sign Out</a></li>
|
<li><a href="{% url logout %}">Sign Out</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -1,33 +1,38 @@
|
|||||||
|
# Django libs
|
||||||
from django.conf.urls.defaults import patterns, include, url
|
from django.conf.urls.defaults import patterns, include, url
|
||||||
from apps.core import views as core_views
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# BR libs
|
||||||
|
from apps.core import views as coreviews
|
||||||
|
from apps.core.auth import views as authviews
|
||||||
|
|
||||||
admin.autodiscover()
|
admin.autodiscover()
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
# Index
|
# Index
|
||||||
url(r'^$', core_views.index, name='index'),
|
url(r'^$', coreviews.index, name='index'),
|
||||||
|
|
||||||
# Static
|
# Static
|
||||||
(r'^s/(?P<path>.*)$', 'django.views.static.serve',
|
(r'^s/(?P<path>.*)$', 'django.views.static.serve',
|
||||||
{'document_root': settings.STATIC_ROOT}),
|
{'document_root': settings.STATIC_ROOT}),
|
||||||
|
|
||||||
# User tasks
|
# User tasks
|
||||||
url(r'^m/profile/?$', core_views.showUserProfile, name='mprofile'),
|
url(r'^m/profile/?$', coreviews.showUserProfile, name='showUserProfile'),
|
||||||
url(r'^m/profile/save/?$', core_views.saveUserProfile, name='msave'),
|
url(r'^m/profile/save/?$', coreviews.saveUserProfile, name='saveUserProfile'),
|
||||||
url(r'^m/profile/edit/?$', core_views.editUserProfile, name='medit'),
|
url(r'^m/profile/edit/?$', coreviews.editUserProfile, name='editUser'),
|
||||||
|
|
||||||
# Auth
|
# Auth
|
||||||
url(r'^logout$', core_views.logout, name='logout'),
|
url(r'^logout$', coreviews.logout, name='logout'),
|
||||||
url(r'^login$', core_views.login, name='login'),
|
url(r'^login$', coreviews.login, name='login'),
|
||||||
url(r'^dologin$', core_views.dologin, name='dologin'),
|
url(r'^dologin$', coreviews.dologin, name='dologin'),
|
||||||
|
url(r'^facebook_connect/?$', authviews.facebook_connect, name='facebook-connect'),
|
||||||
|
|
||||||
# Django
|
# Django
|
||||||
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
||||||
url(r'^admin/', include(admin.site.urls)),
|
url(r'^admin/', include(admin.site.urls)),
|
||||||
|
|
||||||
# Member profile
|
# 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.utils.http import urlquote
|
||||||
from django.http import HttpResponseForbidden
|
from django.http import HttpResponseForbidden
|
||||||
from django.conf import settings
|
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 ajax_login_required(function=None):
|
||||||
def _dec(view_func):
|
def _dec(view_func):
|
||||||
@@ -25,24 +35,6 @@ def ajax_login_required(function=None):
|
|||||||
else:
|
else:
|
||||||
return _dec(function)
|
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):
|
def get_user(request):
|
||||||
if not hasattr(request, 'user'):
|
if not hasattr(request, 'user'):
|
||||||
@@ -106,4 +98,34 @@ def extract_user_agent(request):
|
|||||||
elif 'WP8' in user_agent:
|
elif 'WP8' in user_agent:
|
||||||
platform = 'WP8'
|
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