allauth folder added

This commit is contained in:
Johnson Chetty 2012-12-14 10:58:20 +01:00
parent efa48df80b
commit 6bfa53f1dc
137 changed files with 7150 additions and 0 deletions

0
itf/allauth/__init__.py Normal file
View File

View File

View File

@ -0,0 +1,8 @@
from django.contrib import admin
# from models import PasswordReset
#
# class PasswordResetAdmin(admin.ModelAdmin):
# list_display = ["user", "temp_key", "timestamp", "reset"]
#
# admin.site.register(PasswordReset, PasswordResetAdmin)

View File

@ -0,0 +1,57 @@
import warnings
from django.conf import settings
class AuthenticationMethod:
USERNAME = 'username'
EMAIL = 'email'
USERNAME_EMAIL = 'username_email'
# The user is required to hand over an e-mail address when signing up
EMAIL_REQUIRED = getattr(settings, "ACCOUNT_EMAIL_REQUIRED", False)
# After signing up, keep the user account inactive until the email
# address is verified
EMAIL_VERIFICATION = getattr(settings, "ACCOUNT_EMAIL_VERIFICATION", False)
# Login by email address, not username
if hasattr(settings, "ACCOUNT_EMAIL_AUTHENTICATION"):
warnings.warn("ACCOUNT_EMAIL_AUTHENTICATION is deprecated, use ACCOUNT_AUTHENTICATION_METHOD",
DeprecationWarning)
if getattr(settings, "ACCOUNT_EMAIL_AUTHENTICATION"):
AUTHENTICATION_METHOD = AuthenticationMethod.EMAIL
else:
AUTHENTICATION_METHOD = AuthenticationMethod.USERNAME
else:
AUTHENTICATION_METHOD = getattr(settings, "ACCOUNT_AUTHENTICATION_METHOD",
AuthenticationMethod.USERNAME)
# Enforce uniqueness of e-mail addresses
UNIQUE_EMAIL = getattr(settings, "ACCOUNT_UNIQUE_EMAIL", True)
# Signup password verification
SIGNUP_PASSWORD_VERIFICATION = getattr(settings,
"ACCOUNT_SIGNUP_PASSWORD_VERIFICATION",
True)
# Minimum Password Length
PASSWORD_MIN_LENGTH = getattr(settings, "ACCOUNT_PASSWORD_MIN_LENGTH", 6)
# Subject-line prefix to use for email messages sent
EMAIL_SUBJECT_PREFIX = getattr(settings, "ACCOUNT_EMAIL_SUBJECT_PREFIX", None)
# Signup form
SIGNUP_FORM_CLASS = getattr(settings, "ACCOUNT_SIGNUP_FORM_CLASS", None)
# The user is required to enter a username when signing up
USERNAME_REQUIRED = getattr(settings, "ACCOUNT_USERNAME_REQUIRED", True)
# render_value parameter as passed to PasswordInput fields
PASSWORD_INPUT_RENDER_VALUE = getattr(settings,
"ACCOUNT_PASSWORD_INPUT_RENDER_VALUE",
False)
# If login is by email, email must be required
assert (not AUTHENTICATION_METHOD==AuthenticationMethod.EMAIL) or EMAIL_REQUIRED
# If login includes email, login must be unique
assert (AUTHENTICATION_METHOD==AuthenticationMethod.USERNAME) or UNIQUE_EMAIL
assert (not EMAIL_VERIFICATION) or EMAIL_REQUIRED

View File

@ -0,0 +1,40 @@
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.models import User
from django.db.models import Q
from app_settings import AuthenticationMethod
import app_settings
class AuthenticationBackend(ModelBackend):
def authenticate(self, **credentials):
ret = None
if app_settings.AUTHENTICATION_METHOD == AuthenticationMethod.EMAIL:
ret = self._authenticate_by_email(**credentials)
elif app_settings.AUTHENTICATION_METHOD \
== AuthenticationMethod.USERNAME_EMAIL:
ret = self._authenticate_by_email(**credentials)
if not ret:
ret = self._authenticate_by_username(**credentials)
else:
ret = self._authenticate_by_username(**credentials)
return ret
def _authenticate_by_username(self, **credentials):
return super(AuthenticationBackend, self).authenticate(**credentials)
def _authenticate_by_email(self, **credentials):
# Even though allauth will pass along `email`, other apps may
# not respect this setting. For example, when using
# django-tastypie basic authentication, the login is always
# passed as `username`. So let's place nice with other apps
# and use username as fallback
email = credentials.get('email', credentials.get('username'))
if email:
users = User.objects.filter(Q(email__iexact=email)
| Q(emailaddress__email__iexact
=email))
for user in users:
if user.check_password(credentials["password"]):
return user
return None

View File

@ -0,0 +1,6 @@
from django.conf import settings
def account(request):
return {
"CONTACT_EMAIL": getattr(settings, "CONTACT_EMAIL", "support@example.com")
}

View File

@ -0,0 +1,38 @@
from django.contrib.auth.decorators import login_required
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.shortcuts import render
from emailconfirmation.models import EmailAddress
from utils import send_email_confirmation
def verified_email_required(function=None,
login_url=None,
redirect_field_name=REDIRECT_FIELD_NAME):
"""
Even when email verification is not mandatory during signup, there
may be circumstances during which you really want to prevent
unverified users to proceed. This decorator ensures the user is
authenticated and has a verified email address. If the former is
not the case then the behavior is identical to that of the
standard `login_required` decorator. If the latter does not hold,
email verification mails are automatically resend and the user is
presented with a page informing him he needs to verify his email
address.
"""
def decorator(view_func):
@login_required(redirect_field_name=redirect_field_name,
login_url=login_url)
def _wrapped_view(request, *args, **kwargs):
if not EmailAddress.objects.filter(user=request.user,
verified=True).exists():
send_email_confirmation(request.user, request)
return render(request,
'account/verified_email_required.html')
return view_func(request, *args, **kwargs)
return _wrapped_view
if function:
return decorator(function)
return decorator

View File

@ -0,0 +1,479 @@
import base64
import re
import uuid
from django import forms
from django.conf import settings
from django.core.mail import send_mail
from django.core.urlresolvers import reverse
from django.core import exceptions
from django.db.models import Q
from django.template.loader import render_to_string
from django.utils.translation import ugettext_lazy as _, ugettext
from django.utils.http import int_to_base36
from django.utils.importlib import import_module
from django.contrib import messages
from django.contrib.auth import authenticate
from django.contrib.auth.models import User
from django.contrib.auth.tokens import default_token_generator
from django.contrib.sites.models import Site
from emailconfirmation.models import EmailAddress
# from models import PasswordReset
from utils import perform_login, send_email_confirmation, format_email_subject
from allauth.utils import email_address_exists
from app_settings import AuthenticationMethod
import app_settings
alnum_re = re.compile(r"^\w+$")
class PasswordField(forms.CharField):
def __init__(self, *args, **kwargs):
render_value = kwargs.pop('render_value',
app_settings.PASSWORD_INPUT_RENDER_VALUE)
kwargs['widget'] = forms.PasswordInput(render_value=render_value)
super(PasswordField, self).__init__(*args, **kwargs)
class SetPasswordField(PasswordField):
def clean(self, value):
value = super(SetPasswordField, self).clean(value)
min_length = app_settings.PASSWORD_MIN_LENGTH
if len(value) < min_length:
raise forms.ValidationError(_("Password must be a minimum of {0} "
"characters.").format(min_length))
return value
class LoginForm(forms.Form):
password = PasswordField(
label = _("Password"))
remember = forms.BooleanField(
label = _("Remember Me"),
# help_text = _("If checked you will stay logged in for 3 weeks"),
required = False
)
user = None
def __init__(self, *args, **kwargs):
super(LoginForm, self).__init__(*args, **kwargs)
if app_settings.AUTHENTICATION_METHOD == AuthenticationMethod.EMAIL:
login_widget = forms.TextInput(attrs={'placeholder':
_('E-mail address') })
login_field = forms.EmailField(label=_("E-mail"),
widget=login_widget)
elif app_settings.AUTHENTICATION_METHOD \
== AuthenticationMethod.USERNAME:
login_widget = forms.TextInput(attrs={'placeholder':
_('Username') })
login_field = forms.CharField(label=_("Username"),
widget=login_widget,
max_length=30)
else:
assert app_settings.AUTHENTICATION_METHOD \
== AuthenticationMethod.USERNAME_EMAIL
login_widget = forms.TextInput(attrs={'placeholder':
_('Username or e-mail') })
login_field = forms.CharField(label=ugettext("Login"),
widget=login_widget)
self.fields["login"] = login_field
self.fields.keyOrder = ["login", "password", "remember"]
def user_credentials(self):
"""
Provides the credentials required to authenticate the user for
login.
"""
credentials = {}
login = self.cleaned_data["login"]
if app_settings.AUTHENTICATION_METHOD == AuthenticationMethod.EMAIL:
credentials["email"] = login
elif (app_settings.AUTHENTICATION_METHOD
== AuthenticationMethod.USERNAME):
credentials["username"] = login
else:
if "@" in login and "." in login:
credentials["email"] = login
else:
credentials["username"] = login
credentials["password"] = self.cleaned_data["password"]
return credentials
def clean(self):
if self._errors:
return
user = authenticate(**self.user_credentials())
if user:
if user.is_active:
self.user = user
else:
raise forms.ValidationError(_("This account is currently"
" inactive."))
else:
if app_settings.AUTHENTICATION_METHOD == AuthenticationMethod.EMAIL:
error = _("The e-mail address and/or password you specified"
" are not correct.")
elif app_settings.AUTHENTICATION_METHOD \
== AuthenticationMethod.USERNAME:
error = _("The username and/or password you specified are"
" not correct.")
else:
error = _("The login and/or password you specified are not"
" correct.")
raise forms.ValidationError(error)
return self.cleaned_data
def login(self, request, redirect_url=None):
ret = perform_login(request, self.user, redirect_url=redirect_url)
if self.cleaned_data["remember"]:
request.session.set_expiry(60 * 60 * 24 * 7 * 3)
else:
request.session.set_expiry(0)
return ret
class _DummyCustomSignupForm(forms.Form):
def save(self, user):
pass
def _base_signup_form_class():
if not app_settings.SIGNUP_FORM_CLASS:
return _DummyCustomSignupForm
try:
fc_module, fc_classname = app_settings.SIGNUP_FORM_CLASS.rsplit('.', 1)
except ValueError:
raise exceptions.ImproperlyConfigured('%s does not point to a form'
' class'
% app_settings.SIGNUP_FORM_CLASS)
try:
mod = import_module(fc_module)
except ImportError, e:
raise exceptions.ImproperlyConfigured('Error importing form class %s:'
' "%s"' % (fc_module, e))
try:
fc_class = getattr(mod, fc_classname)
except AttributeError:
raise exceptions.ImproperlyConfigured('Module "%s" does not define a'
' "%s" class' % (fc_module,
fc_classname))
if not hasattr(fc_class, 'save'):
raise exceptions.ImproperlyConfigured('The custom signup form must'
' implement a "save" method')
return fc_class
class BaseSignupForm(_base_signup_form_class()):
username = forms.CharField(
label = _("Username"),
max_length = 30,
widget = forms.TextInput()
)
email = forms.EmailField(widget=forms.TextInput())
def __init__(self, *args, **kwargs):
super(BaseSignupForm, self).__init__(*args, **kwargs)
if (app_settings.EMAIL_REQUIRED or
app_settings.EMAIL_VERIFICATION or
app_settings.AUTHENTICATION_METHOD == AuthenticationMethod.EMAIL):
self.fields["email"].label = ugettext("E-mail")
self.fields["email"].required = True
else:
self.fields["email"].label = ugettext("E-mail (optional)")
self.fields["email"].required = False
if not app_settings.USERNAME_REQUIRED:
del self.fields["username"]
def random_username(self):
return base64.urlsafe_b64encode(uuid.uuid4().bytes).strip('=')
def clean_username(self):
value = self.cleaned_data["username"]
if not alnum_re.search(value):
raise forms.ValidationError(_("Usernames can only contain "
"letters, numbers and underscores."))
try:
User.objects.get(username__iexact=value)
except User.DoesNotExist:
return value
raise forms.ValidationError(_("This username is already taken. Please "
"choose another."))
def clean_email(self):
value = self.cleaned_data["email"]
if app_settings.UNIQUE_EMAIL:
if value and email_address_exists(value):
raise forms.ValidationError \
(_("A user is registered with this e-mail address."))
return value
def create_user(self, commit=True):
user = User()
# data collected by providers, if any, is passed as `initial`
# signup form data. This may contain fields such as
# `first_name`, whereas these may not have field counterparts
# in the form itself. So let's pick these up here...
data = self.initial
user.last_name = data.get('last_name', '')
user.first_name = data.get('first_name', '')
if app_settings.USERNAME_REQUIRED:
user.username = self.cleaned_data["username"]
else:
while True:
user.username = self.random_username()
try:
User.objects.get(username=user.username)
except User.DoesNotExist:
break
user.email = self.cleaned_data["email"].strip().lower()
user.set_unusable_password()
if commit:
user.save()
return user
class SignupForm(BaseSignupForm):
password1 = SetPasswordField(label=_("Password"))
password2 = PasswordField(label=_("Password (again)"))
confirmation_key = forms.CharField(
max_length = 40,
required = False,
widget = forms.HiddenInput())
def __init__(self, *args, **kwargs):
super(SignupForm, self).__init__(*args, **kwargs)
current_order =self.fields.keyOrder
preferred_order = self.fields.keyOrder = ["username",
"password1",
"password2",
"email"]
if not app_settings.USERNAME_REQUIRED:
preferred_order = self.fields.keyOrder = ["email",
"password1",
"password2"]
# Make sure custom fields are put below main signup fields
self.fields.keyOrder = preferred_order + [ f for f in current_order if not f in preferred_order ]
if not app_settings.SIGNUP_PASSWORD_VERIFICATION:
del self.fields["password2"]
def clean(self):
super(SignupForm, self).clean()
if app_settings.SIGNUP_PASSWORD_VERIFICATION \
and "password1" in self.cleaned_data \
and "password2" in self.cleaned_data:
if self.cleaned_data["password1"] != self.cleaned_data["password2"]:
raise forms.ValidationError(_("You must type the same password each time."))
return self.cleaned_data
def create_user(self, commit=True):
user = super(SignupForm, self).create_user(commit=False)
password = self.cleaned_data.get("password1")
if password:
user.set_password(password)
if commit:
user.save()
return user
def save(self, request=None):
# don't assume a username is available. it is a common removal if
# site developer wants to use e-mail authentication.
email = self.cleaned_data["email"]
if self.cleaned_data.get("confirmation_key"):
from friends.models import JoinInvitation # @@@ temporary fix for issue 93
try:
join_invitation = JoinInvitation.objects.get(confirmation_key=self.cleaned_data["confirmation_key"])
confirmed = True
except JoinInvitation.DoesNotExist:
confirmed = False
else:
confirmed = False
new_user = self.create_user()
super(SignupForm, self).save(new_user, request)
# @@@ clean up some of the repetition below -- DRY!
if confirmed:
if email == join_invitation.contact.email:
join_invitation.accept(new_user) # should go before creation of EmailAddress below
if request:
messages.add_message(request, messages.INFO,
ugettext(u"Your e-mail address has already been verified")
)
# already verified so can just create
EmailAddress(user=new_user, email=email, verified=True, primary=True).save()
else:
join_invitation.accept(new_user) # should go before creation of EmailAddress below
if email:
if request:
messages.add_message(request, messages.INFO,
ugettext(u"Confirmation e-mail sent to %(email)s") % {
"email": email,
}
)
EmailAddress.objects.add_email(new_user, email)
else:
send_email_confirmation(new_user, request=request)
self.after_signup(new_user)
return new_user
def after_signup(self, user, **kwargs):
"""
An extension point for subclasses.
"""
pass
class UserForm(forms.Form):
def __init__(self, user=None, *args, **kwargs):
self.user = user
super(UserForm, self).__init__(*args, **kwargs)
class AddEmailForm(UserForm):
email = forms.EmailField(
label = _("E-mail"),
required = True,
widget = forms.TextInput(attrs={"size": "30"})
)
def clean_email(self):
value = self.cleaned_data["email"]
errors = {
"this_account": _("This e-mail address already associated with this account."),
"different_account": _("This e-mail address already associated with another account."),
}
emails = EmailAddress.objects.filter(email__iexact=value)
if emails.filter(user=self.user).exists():
raise forms.ValidationError(errors["this_account"])
if app_settings.UNIQUE_EMAIL:
if emails.exclude(user=self.user).exists():
raise forms.ValidationError(errors["different_account"])
return value
def save(self):
return EmailAddress.objects.add_email(self.user, self.cleaned_data["email"])
class ChangePasswordForm(UserForm):
oldpassword = PasswordField(label=_("Current Password"))
password1 = SetPasswordField(label=_("New Password"))
password2 = PasswordField(label=_("New Password (again)"))
def clean_oldpassword(self):
if not self.user.check_password(self.cleaned_data.get("oldpassword")):
raise forms.ValidationError(_("Please type your current password."))
return self.cleaned_data["oldpassword"]
def clean_password2(self):
if "password1" in self.cleaned_data and "password2" in self.cleaned_data:
if self.cleaned_data["password1"] != self.cleaned_data["password2"]:
raise forms.ValidationError(_("You must type the same password each time."))
return self.cleaned_data["password2"]
def save(self):
self.user.set_password(self.cleaned_data["password1"])
self.user.save()
class SetPasswordForm(UserForm):
password1 = SetPasswordField(label=_("Password"))
password2 = PasswordField(label=_("Password (again)"))
def clean_password2(self):
if "password1" in self.cleaned_data and "password2" in self.cleaned_data:
if self.cleaned_data["password1"] != self.cleaned_data["password2"]:
raise forms.ValidationError(_("You must type the same password each time."))
return self.cleaned_data["password2"]
def save(self):
self.user.set_password(self.cleaned_data["password1"])
self.user.save()
class ResetPasswordForm(forms.Form):
email = forms.EmailField(
label = _("E-mail"),
required = True,
widget = forms.TextInput(attrs={"size":"30"})
)
def clean_email(self):
email = self.cleaned_data["email"]
self.users = User.objects.filter(Q(email__iexact=email)
| Q(emailaddress__email__iexact=email)).distinct()
if not self.users.exists():
raise forms.ValidationError(_("The e-mail address is not assigned to any user account"))
return self.cleaned_data["email"]
def save(self, **kwargs):
email = self.cleaned_data["email"]
token_generator = kwargs.get("token_generator", default_token_generator)
for user in self.users:
temp_key = token_generator.make_token(user)
# save it to the password reset model
# password_reset = PasswordReset(user=user, temp_key=temp_key)
# password_reset.save()
current_site = Site.objects.get_current()
# send the password reset email
subject = format_email_subject(_("Password Reset E-mail"))
path = reverse("account_reset_password_from_key",
kwargs=dict(uidb36=int_to_base36(user.id),
key=temp_key))
url = 'http://%s%s' % (current_site.domain,
path)
message = render_to_string \
("account/password_reset_key_message.txt",
{ "site": current_site,
"user": user,
"password_reset_url": url })
send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [email])
return self.cleaned_data["email"]
class ResetPasswordKeyForm(forms.Form):
password1 = SetPasswordField(label=_("New Password"))
password2 = PasswordField(label=_("New Password (again)"))
def __init__(self, *args, **kwargs):
self.user = kwargs.pop("user", None)
self.temp_key = kwargs.pop("temp_key", None)
super(ResetPasswordKeyForm, self).__init__(*args, **kwargs)
# FIXME: Inspecting other fields -> should be put in def clean(self) ?
def clean_password2(self):
if "password1" in self.cleaned_data and "password2" in self.cleaned_data:
if self.cleaned_data["password1"] != self.cleaned_data["password2"]:
raise forms.ValidationError(_("You must type the same password each time."))
return self.cleaned_data["password2"]
def save(self):
# set the new user password
user = self.user
user.set_password(self.cleaned_data["password1"])
user.save()
# mark password reset object as reset
# PasswordReset.objects.filter(temp_key=self.temp_key).update(reset=True)

View File

@ -0,0 +1,11 @@
from emailconfirmation.models import EmailConfirmation
from emailconfirmation.signals import email_confirmed
def mark_user_active(sender, instance=None, **kwargs):
user = kwargs.get("email_address").user
user.is_active = True
user.save()
email_confirmed.connect(mark_user_active, sender=EmailConfirmation)

View File

@ -0,0 +1,4 @@
import django.dispatch
user_logged_in = django.dispatch.Signal(providing_args=["request", "user"])

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,47 @@
from django import template
from allauth.account.utils import user_display
register = template.Library()
class UserDisplayNode(template.Node):
def __init__(self, user, as_var=None):
self.user_var = template.Variable(user)
self.as_var = as_var
def render(self, context):
user = self.user_var.resolve(context)
display = user_display(user)
if self.as_var:
context[self.as_var] = display
return ""
return display
@register.tag(name="user_display")
def do_user_display(parser, token):
"""
Example usage::
{% user_display user %}
or if you need to use in a {% blocktrans %}::
{% user_display user as user_display %}
{% blocktrans %}{{ user_display }} has sent you a gift.{% endblocktrans %}
"""
bits = token.split_contents()
if len(bits) == 2:
user = bits[1]
as_var = None
elif len(bits) == 4:
user = bits[1]
as_var = bits[3]
else:
raise template.TemplateSyntaxError("'%s' takes either two or four arguments" % bits[0])
return UserDisplayNode(user, as_var)

View File

@ -0,0 +1,98 @@
from datetime import timedelta
try:
from django.utils.timezone import now
except ImportError:
from datetime import datetime
now = datetime.now
from django.test import TestCase
from django.conf import settings
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.test.client import Client
from django.core import mail
from django.contrib.sites.models import Site
from emailconfirmation.models import EmailAddress, EmailConfirmation
from app_settings import AuthenticationMethod
import app_settings
class AccountTests(TestCase):
def setUp(self):
self.OLD_EMAIL_VERIFICATION = app_settings.EMAIL_VERIFICATION
self.OLD_AUTHENTICATION_METHOD = app_settings.AUTHENTICATION_METHOD
self.OLD_SIGNUP_FORM_CLASS = app_settings.SIGNUP_FORM_CLASS
self.OLD_USERNAME_REQUIRED = app_settings.USERNAME_REQUIRED
app_settings.EMAIL_VERIFICATION = True
app_settings.AUTHENTICATION_METHOD = AuthenticationMethod.USERNAME
app_settings.SIGNUP_FORM_CLASS = None
app_settings.USERNAME_REQUIRED = True
if 'allauth.socialaccount' in settings.INSTALLED_APPS:
# Otherwise ImproperlyConfigured exceptions may occur
from ..socialaccount.models import SocialApp
SocialApp.objects.create(name='testfb',
provider='facebook',
site=Site.objects.get_current())
def test_email_verification_mandatory(self):
c = Client()
# Signup
resp = c.post(reverse('account_signup'),
{ 'username': 'johndoe',
'email': 'john@doe.com',
'password1': 'johndoe',
'password2': 'johndoe' })
self.assertEquals(resp.status_code, 200)
self.assertEquals(mail.outbox[0].to, ['john@doe.com'])
self.assertEquals(len(mail.outbox), 1)
self.assertTemplateUsed(resp,
'account/verification_sent.html')
# Attempt to login, unverified
for attempt in [1, 2]:
resp = c.post(reverse('account_login'),
{ 'login': 'johndoe',
'password': 'johndoe'})
# is_active is controlled by the admin to manually disable
# users. I don't want this flag to flip automatically whenever
# users verify their email adresses.
self.assertTrue(User.objects.filter(username='johndoe',
is_active=True).exists())
self.assertTemplateUsed(resp,
'account/verification_sent.html')
self.assertEquals(len(mail.outbox), attempt)
# Wait for cooldown
EmailConfirmation.objects.update(sent=now()
- timedelta(days=1))
# Verify, and re-attempt to login.
EmailAddress.objects.filter(user__username='johndoe') \
.update(verified=True)
resp = c.post(reverse('account_login'),
{ 'login': 'johndoe',
'password': 'johndoe'})
self.assertEquals(resp['location'],
'http://testserver'+settings.LOGIN_REDIRECT_URL)
def x_test_email_escaping(self):
"""
Test is only valid if emailconfirmation is listed after
allauth in INSTALLED_APPS
"""
site = Site.objects.get_current()
site.name = '<enc&"test>'
site.save()
u = User.objects.create(username='test',
email='foo@bar.com')
EmailAddress.objects.add_email(u, u.email)
def tearDown(self):
app_settings.EMAIL_VERIFICATION = self.OLD_EMAIL_VERIFICATION
app_settings.AUTHENTICATION_METHOD = self.OLD_AUTHENTICATION_METHOD
app_settings.SIGNUP_FORM_CLASS = self.OLD_SIGNUP_FORM_CLASS
app_settings.USERNAME_REQUIRED = self.OLD_USERNAME_REQUIRED

View File

@ -0,0 +1,24 @@
from django.conf.urls.defaults import patterns, url
import views
urlpatterns = patterns("",
url(r"^email/$", views.email, name="account_email"),
url(r"^signup/$", views.signup, name="account_signup"),
url(r"^login/$", views.login, name="account_login"),
url(r"^password/change/$", views.password_change, name="account_change_password"),
url(r"^password/set/$", views.password_set, name="account_set_password"),
# url(r"^password_delete/$", views.password_delete, name="acct_passwd_delete"),
# url(r"^password_delete/done/$", "django.views.generic.simple.direct_to_template", {
# "template": "account/password_delete_done.html",
# }, name="acct_passwd_delete_done"),
url(r"^logout/$", views.logout, name="account_logout"),
url(r"^confirm_email/(\w+)/$", "emailconfirmation.views.confirm_email", name="account_confirm_email"),
# password reset
url(r"^password/reset/$", views.password_reset, name="account_reset_password"),
url(r"^password/reset/done/$", views.password_reset_done, name="account_reset_password_done"),
url(r"^password/reset/key/(?P<uidb36>[0-9A-Za-z]+)-(?P<key>.+)/$", views.password_reset_from_key, name="account_reset_password_from_key"),
)

View File

@ -0,0 +1,162 @@
from datetime import timedelta
try:
from django.utils.timezone import now
except ImportError:
from datetime import datetime
now = datetime.now
from django.contrib import messages
from django.shortcuts import render
from django.contrib.sites.models import Site
from django.conf import settings
from django.core.urlresolvers import reverse
from django.contrib.auth import login
from django.utils.translation import ugettext_lazy as _, ugettext
from django.http import HttpResponseRedirect
from emailconfirmation.models import EmailAddress, EmailConfirmation
from allauth.utils import import_callable
from signals import user_logged_in
import app_settings
LOGIN_REDIRECT_URLNAME = getattr(settings, "LOGIN_REDIRECT_URLNAME", "")
def get_default_redirect(request, redirect_field_name="next",
login_redirect_urlname=LOGIN_REDIRECT_URLNAME, session_key_value="redirect_to"):
"""
Returns the URL to be used in login procedures by looking at different
values in the following order:
- a REQUEST value, GET or POST, named "next" by default.
- LOGIN_REDIRECT_URL - the URL in the setting
- LOGIN_REDIRECT_URLNAME - the name of a URLconf entry in the settings
"""
if login_redirect_urlname:
default_redirect_to = reverse(login_redirect_urlname)
else:
default_redirect_to = settings.LOGIN_REDIRECT_URL
redirect_to = request.REQUEST.get(redirect_field_name)
if not redirect_to:
# try the session if available
if hasattr(request, "session"):
redirect_to = request.session.get(session_key_value)
# light security check -- make sure redirect_to isn't garabage.
if not redirect_to or "://" in redirect_to or " " in redirect_to:
redirect_to = default_redirect_to
return redirect_to
_user_display_callable = None
def user_display(user):
global _user_display_callable
if not _user_display_callable:
f = getattr(settings, "ACCOUNT_USER_DISPLAY",
lambda user: user.username)
_user_display_callable = import_callable(f)
return _user_display_callable(user)
# def has_openid(request):
# """
# Given a HttpRequest determine whether the OpenID on it is associated thus
# allowing caller to know whether OpenID is good to depend on.
# """
# from django_openid.models import UserOpenidAssociation
# for association in UserOpenidAssociation.objects.filter(user=request.user):
# if association.openid == unicode(request.openid):
# return True
# return False
def perform_login(request, user, redirect_url=None):
# not is_active: social users are redirected to a template
# local users are stopped due to form validation checking is_active
assert user.is_active
if (app_settings.EMAIL_VERIFICATION
and not EmailAddress.objects.filter(user=user,
verified=True).exists()):
send_email_confirmation(user, request=request)
return render(request,
"account/verification_sent.html",
{ "email": user.email })
# HACK: This may not be nice. The proper Django way is to use an
# authentication backend, but I fail to see any added benefit
# whereas I do see the downsides (having to bother the integrator
# to set up authentication backends in settings.py
if not hasattr(user, 'backend'):
user.backend = "django.contrib.auth.backends.ModelBackend"
user_logged_in.send(sender=user.__class__, request=request, user=user)
login(request, user)
messages.add_message(request, messages.SUCCESS,
ugettext("Successfully signed in as %(user)s.") % { "user": user_display(user) } )
if not redirect_url:
redirect_url = get_default_redirect(request)
return HttpResponseRedirect(redirect_url)
def complete_signup(request, user, success_url):
return perform_login(request, user, redirect_url=success_url)
def send_email_confirmation(user, request=None):
"""
E-mail verification mails are sent:
a) Explicitly: when a user signs up
b) Implicitly: when a user attempts to log in using an unverified
e-mail while EMAIL_VERIFICATION is mandatory.
Especially in case of b), we want to limit the number of mails
sent (consider a user retrying a few times), which is why there is
a cooldown period before sending a new mail.
"""
COOLDOWN_PERIOD = timedelta(minutes=3)
email = user.email
if email:
try:
email_address = EmailAddress.objects.get(user=user,
email__iexact=email)
email_confirmation_sent = EmailConfirmation.objects \
.filter(sent__gt=now() - COOLDOWN_PERIOD,
email_address=email_address) \
.exists()
if not email_confirmation_sent:
EmailConfirmation.objects.send_confirmation(email_address)
except EmailAddress.DoesNotExist:
EmailAddress.objects.add_email(user, user.email)
email_confirmation_sent = False
if request and not email_confirmation_sent:
messages.info(request,
_(u"Confirmation e-mail sent to %(email)s") % {"email": email}
)
def format_email_subject(subject):
prefix = app_settings.EMAIL_SUBJECT_PREFIX
if prefix is None:
site = Site.objects.get_current()
prefix = "[{name}] ".format(name=site.name)
return prefix + unicode(subject)
def sync_user_email_addresses(user):
"""
Keep user.email in sync with user.emailadress_set.
Under some circumstances the user.email may not have ended up as
an EmailAddress record, e.g. in the case of manually created admin
users.
"""
if user.email and not EmailAddress.objects.filter(user=user,
email=user.email).exists():
EmailAddress.objects.create(user=user,
email=user.email,
primary=False,
verified=False)

View File

@ -0,0 +1,250 @@
from django.core.urlresolvers import reverse
from django.contrib.sites.models import Site
from django.http import HttpResponseRedirect, Http404
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
from django.utils.http import base36_to_int
from django.utils.translation import ugettext
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.contrib.auth.tokens import default_token_generator
from emailconfirmation.models import EmailAddress, EmailConfirmation
from allauth.utils import passthrough_login_redirect_url
from utils import get_default_redirect, complete_signup
from forms import AddEmailForm, ChangePasswordForm
from forms import LoginForm, ResetPasswordKeyForm
from forms import ResetPasswordForm, SetPasswordForm, SignupForm
from utils import sync_user_email_addresses
def login(request, **kwargs):
form_class = kwargs.pop("form_class", LoginForm)
template_name = kwargs.pop("template_name", "account/login.html")
success_url = kwargs.pop("success_url", None)
url_required = kwargs.pop("url_required", False)
extra_context = kwargs.pop("extra_context", {})
redirect_field_name = kwargs.pop("redirect_field_name", "next")
if extra_context is None:
extra_context = {}
if success_url is None:
success_url = get_default_redirect(request, redirect_field_name)
if request.method == "POST" and not url_required:
form = form_class(request.POST)
if form.is_valid():
return form.login(request, redirect_url=success_url)
else:
form = form_class()
ctx = {
"form": form,
"signup_url": passthrough_login_redirect_url(request,
reverse("account_signup")),
"site": Site.objects.get_current(),
"url_required": url_required,
"redirect_field_name": redirect_field_name,
"redirect_field_value": request.REQUEST.get(redirect_field_name),
}
ctx.update(extra_context)
return render_to_response(template_name, RequestContext(request, ctx))
def signup(request, **kwargs):
form_class = kwargs.pop("form_class", SignupForm)
template_name = kwargs.pop("template_name", "account/signup.html")
redirect_field_name = kwargs.pop("redirect_field_name", "next")
success_url = kwargs.pop("success_url", None)
if success_url is None:
success_url = get_default_redirect(request, redirect_field_name)
if request.method == "POST":
form = form_class(request.POST)
if form.is_valid():
user = form.save(request=request)
return complete_signup(request, user, success_url)
else:
form = form_class()
ctx = {"form": form,
"login_url": passthrough_login_redirect_url(request,
reverse("account_login")),
"redirect_field_name": redirect_field_name,
"redirect_field_value": request.REQUEST.get(redirect_field_name) }
return render_to_response(template_name, RequestContext(request, ctx))
@login_required
def email(request, **kwargs):
form_class = kwargs.pop("form_class", AddEmailForm)
template_name = kwargs.pop("template_name", "account/email.html")
sync_user_email_addresses(request.user)
if request.method == "POST" and request.user.is_authenticated():
if request.POST.has_key("action_add"):
add_email_form = form_class(request.user, request.POST)
if add_email_form.is_valid():
add_email_form.save()
messages.add_message(request, messages.INFO,
ugettext(u"Confirmation e-mail sent to %(email)s") % {
"email": add_email_form.cleaned_data["email"]
}
)
add_email_form = form_class() # @@@
else:
add_email_form = form_class()
if request.POST.get("email"):
if request.POST.has_key("action_send"):
email = request.POST["email"]
try:
email_address = EmailAddress.objects.get(
user=request.user,
email=email,
)
messages.add_message(request, messages.INFO,
ugettext("Confirmation e-mail sent to %(email)s") % {
"email": email,
}
)
EmailConfirmation.objects.send_confirmation(email_address)
except EmailAddress.DoesNotExist:
pass
elif request.POST.has_key("action_remove"):
email = request.POST["email"]
try:
email_address = EmailAddress.objects.get(
user=request.user,
email=email
)
email_address.delete()
messages.add_message(request, messages.SUCCESS,
ugettext("Removed e-mail address %(email)s") % {
"email": email,
}
)
except EmailAddress.DoesNotExist:
pass
elif request.POST.has_key("action_primary"):
email = request.POST["email"]
email_address = EmailAddress.objects.get(
user=request.user,
email=email,
)
email_address.set_as_primary()
messages.add_message(request, messages.SUCCESS,
ugettext("Primary e-mail address set"))
else:
add_email_form = form_class()
ctx = { "add_email_form": add_email_form }
return render_to_response(template_name, RequestContext(request, ctx))
@login_required
def password_change(request, **kwargs):
form_class = kwargs.pop("form_class", ChangePasswordForm)
template_name = kwargs.pop("template_name", "account/password_change.html")
if not request.user.has_usable_password():
return HttpResponseRedirect(reverse(password_set))
if request.method == "POST":
password_change_form = form_class(request.user, request.POST)
if password_change_form.is_valid():
password_change_form.save()
messages.add_message(request, messages.SUCCESS,
ugettext(u"Password successfully changed.")
)
password_change_form = form_class(request.user)
else:
password_change_form = form_class(request.user)
ctx = { "password_change_form": password_change_form }
return render_to_response(template_name, RequestContext(request, ctx))
@login_required
def password_set(request, **kwargs):
form_class = kwargs.pop("form_class", SetPasswordForm)
template_name = kwargs.pop("template_name", "account/password_set.html")
if request.user.has_usable_password():
return HttpResponseRedirect(reverse(password_change))
if request.method == "POST":
password_set_form = form_class(request.user, request.POST)
if password_set_form.is_valid():
password_set_form.save()
messages.add_message(request, messages.SUCCESS,
ugettext(u"Password successfully set.")
)
return HttpResponseRedirect(reverse(password_change))
else:
password_set_form = form_class(request.user)
ctx = { "password_set_form": password_set_form }
return render_to_response(template_name, RequestContext(request, ctx))
def password_reset(request, **kwargs):
form_class = kwargs.pop("form_class", ResetPasswordForm)
template_name = kwargs.pop("template_name", "account/password_reset.html")
if request.method == "POST":
password_reset_form = form_class(request.POST)
if password_reset_form.is_valid():
password_reset_form.save()
return HttpResponseRedirect(reverse(password_reset_done))
else:
password_reset_form = form_class()
return render_to_response(template_name, RequestContext(request, { "password_reset_form": password_reset_form, }))
def password_reset_done(request, **kwargs):
return render_to_response(kwargs.pop("template_name", "account/password_reset_done.html"), RequestContext(request, {}))
def password_reset_from_key(request, uidb36, key, **kwargs):
form_class = kwargs.get("form_class", ResetPasswordKeyForm)
template_name = kwargs.get("template_name", "account/password_reset_from_key.html")
token_generator = kwargs.get("token_generator", default_token_generator)
# pull out user
try:
uid_int = base36_to_int(uidb36)
except ValueError:
raise Http404
user = get_object_or_404(User, id=uid_int)
if token_generator.check_token(user, key):
if request.method == "POST":
password_reset_key_form = form_class(request.POST, user=user, temp_key=key)
if password_reset_key_form.is_valid():
password_reset_key_form.save()
messages.add_message(request, messages.SUCCESS,
ugettext(u"Password successfully changed.")
)
password_reset_key_form = None
else:
password_reset_key_form = form_class()
ctx = { "form": password_reset_key_form, }
else:
ctx = { "token_fail": True, }
return render_to_response(template_name, RequestContext(request, ctx))
def logout(request, **kwargs):
messages.add_message(request, messages.SUCCESS,
ugettext("You have signed out.")
)
kwargs['template_name'] = kwargs.pop('template_name', 'account/logout.html')
from django.contrib.auth.views import logout as _logout
return _logout(request, **kwargs)

View File

@ -0,0 +1,10 @@
from django.conf import settings
SOCIALACCOUNT_ENABLED = 'allauth.socialaccount' in settings.INSTALLED_APPS
if SOCIALACCOUNT_ENABLED:
assert 'allauth.socialaccount.context_processors.socialaccount' \
in settings.TEMPLATE_CONTEXT_PROCESSORS
LOGIN_REDIRECT_URL = getattr(settings, 'LOGIN_REDIRECT_URL', '/')

Binary file not shown.

View File

@ -0,0 +1,689 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-06-22 23:00+0200\n"
"PO-Revision-Date: 2012-06-22 23:04+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: account/forms.py:48
msgid "Password must be a minimum of {0} characters."
msgstr "Het wachtwoord moet minimaal {0} tekens bevatten."
#: account/forms.py:55 account/forms.py:243 account/forms.py:393
msgid "Password"
msgstr "Wachtwoord"
#: account/forms.py:57
msgid "Remember Me"
msgstr "Onthouden"
#: account/forms.py:68
msgid "E-mail address"
msgstr "E-mail adres"
#: account/forms.py:69 account/forms.py:185 account/forms.py:346
#: account/forms.py:410
msgid "E-mail"
msgstr "E-mail"
#: account/forms.py:74 account/forms.py:75 account/forms.py:174
msgid "Username"
msgstr "Gebruikersnaam"
#: account/forms.py:82
msgid "Username or e-mail"
msgstr "Gebruikersnaam of e-mail"
#: account/forms.py:83
msgid "Login"
msgstr "Inloggen"
#: account/forms.py:116
msgid "This account is currently inactive."
msgstr "Dit account is niet actief"
#: account/forms.py:120
msgid "The e-mail address and/or password you specified are not correct."
msgstr "Je e-mail adres en wachtwoord komen niet overeen."
#: account/forms.py:124
msgid "The username and/or password you specified are not correct."
msgstr "Je gebruikersnaam en wachtwoord komen niet overeen."
#: account/forms.py:127
msgid "The login and/or password you specified are not correct."
msgstr "Je login en wachtwoord komen niet overeen."
#: account/forms.py:188
msgid "E-mail (optional)"
msgstr "E-mail (optioneel)"
#: account/forms.py:199
msgid "Usernames can only contain letters, numbers and underscores."
msgstr ""
"Gebruikersnamen mogen alleen letters, cijfers en liggende streepjes (_) "
"bevatten."
#: account/forms.py:205
msgid "This username is already taken. Please choose another."
msgstr "Deze gebruikersnaam is al in gebruik. Kies a.u.b. een andere naam."
#: account/forms.py:213
msgid "A user is registered with this e-mail address."
msgstr "Er is al een gebruiker geregistreerd met dit e-mail adres."
#: account/forms.py:244 account/forms.py:394
msgid "Password (again)"
msgstr "Wachtwoord (bevestigen)"
#: account/forms.py:272 account/forms.py:383 account/forms.py:399
#: account/forms.py:468
msgid "You must type the same password each time."
msgstr "Je moet hetzelfde wachtwoord twee keer intoetsen."
#: account/forms.py:308
msgid "Your e-mail address has already been verified"
msgstr "Je e-mail adres is al geverifieerd"
#: account/forms.py:317 account/utils.py:135 account/views.py:93
#: account/views.py:109
#, python-format
msgid "Confirmation e-mail sent to %(email)s"
msgstr "Bevestigings e-mail verzonden aan %(email)s"
#: account/forms.py:354
msgid "This e-mail address already associated with this account."
msgstr "Dit e-mail adres is al geassocieerd met dit account."
#: account/forms.py:355
msgid "This e-mail address already associated with another account."
msgstr "Dit e-mail adres is al geassocieerd met een ander account."
#: account/forms.py:371
msgid "Current Password"
msgstr "Huidig wachtwoord"
#: account/forms.py:372 account/forms.py:456
msgid "New Password"
msgstr "Nieuw wachtwoord"
#: account/forms.py:373 account/forms.py:457
msgid "New Password (again)"
msgstr "Nieuw wachtwoord (bevestigen)"
#: account/forms.py:377
msgid "Please type your current password."
msgstr "Geef je huidige wachtwoord op."
#: account/forms.py:420
msgid "The e-mail address is not assigned to any user account"
msgstr "Dit e-mail adres is niet bij ons bekend"
#: account/forms.py:439
msgid "Password Reset E-mail"
msgstr "Nieuw wachtwoord"
#: account/utils.py:96
#, python-format
msgid "Successfully signed in as %(user)s."
msgstr "Je bent nu ingelogd als %(user)s."
#: account/views.py:125
#, python-format
msgid "Removed e-mail address %(email)s"
msgstr "E-mail adres %(email)s verwijderd"
#: account/views.py:139
msgid "Primary e-mail address set"
msgstr "Primair e-mail adres ingesteld"
#: account/views.py:160 account/views.py:233
msgid "Password successfully changed."
msgstr "Wachtwoord wijziging geslaagd."
#: account/views.py:183
msgid "Password successfully set."
msgstr "Je wachtwoord is gewijzigd."
#: account/views.py:247 templates/account/logout.html:10
msgid "You have signed out."
msgstr "Je bent afgemeld."
#: socialaccount/forms.py:34
msgid "Your local account has no password setup."
msgstr "Je account heeft geen wachtwoord ingesteld."
#: socialaccount/forms.py:38
msgid "Your local account has no verified e-mail address."
msgstr "Je account heeft geen geverifieerd e-mail adres."
#: socialaccount/helpers.py:108
msgid "The social account has been connected to your existing account"
msgstr "Het externe account is gekoppeld aan je bestaande account"
#: socialaccount/views.py:58
msgid "The social account has been disconnected"
msgstr "Het externe account is ontkoppeld"
#: socialaccount/providers/oauth/client.py:79
#, python-format
msgid "Invalid response while obtaining request token from \"%s\"."
msgstr ""
"Ongeldig antwoord ontvangen tijdens het ophalen van een verzoeksleutel van "
"\"%s\"."
#: socialaccount/providers/oauth/client.py:102
#, python-format
msgid "Invalid response while obtaining access token from \"%s\"."
msgstr ""
"Ongeldig antwoord ontvangen tijdens het ophalen van een toegangssleutel van "
"\"%s\"."
#: socialaccount/providers/oauth/client.py:115
#, python-format
msgid "No request token saved for \"%s\"."
msgstr "Geen verzoeksleutel opgeslagen voor \"%s\"."
#: socialaccount/providers/oauth/client.py:163
#, python-format
msgid "No access token saved for \"%s\"."
msgstr "Geen toegangssleutel opgeslagen voor \"%s\"."
#: socialaccount/providers/oauth/client.py:183
#, python-format
msgid "No access to private resources at \"%s\"."
msgstr "Geen toegang tot prive data bij \"%s\"."
#: templates/account/email.html:5
msgid "Account"
msgstr "Account"
#: templates/account/email.html:8
msgid "E-mail Addresses"
msgstr "E-mail adressen"
#: templates/account/email.html:10
msgid "The following e-mail addresses are associated to your account:"
msgstr "De volgende e-mail adressen zijn gekoppeld aan jouw account:"
#: templates/account/email.html:24
msgid "Verified"
msgstr "Geverifieerd"
#: templates/account/email.html:26
msgid "Unverified"
msgstr "Ongeverifieerd"
#: templates/account/email.html:28
msgid "Primary"
msgstr "Primair"
#: templates/account/email.html:34
msgid "Make Primary"
msgstr "Maak primair"
#: templates/account/email.html:35
msgid "Re-send Verification"
msgstr "Stuur verificatie e-mail opnieuw"
#: templates/account/email.html:36
msgid "Remove"
msgstr "Verwijder"
#: templates/account/email.html:43
msgid "Warning:"
msgstr "Waarschuwing:"
#: templates/account/email.html:43
msgid ""
"You currently do not have any e-mail address set up. You should really add "
"an e-mail address so you can receive notifications, reset your password, etc."
msgstr ""
"Het is raadzaam een e-mail adres toe te voegen zodat je notificaties kunt "
"ontvangen, je wachtwoord opnieuw kunt instellen, enz."
#: templates/account/email.html:48
msgid "Add E-mail Address"
msgstr "Voeg e-mail adres toe"
#: templates/account/email.html:53
msgid "Add E-mail"
msgstr "E-mail toevoegen"
#: templates/account/email.html:63
msgid "Do you really want to remove the selected e-mail address?"
msgstr "Wil je het geselecteerde e-mail adres echt verwijderen?"
#: templates/account/login.html:6 templates/account/login.html.py:11
#: templates/account/login.html:43
msgid "Sign In"
msgstr "Aanmelden"
#: templates/account/login.html:16
#, python-format
msgid ""
"Please sign in with one\n"
"of your existing third party accounts. Or, <a \n"
"href=\"%(signup_url)s\">sign up</a> for a %(site_name)s account and sign in\n"
"below:"
msgstr ""
"Meld je aan met een van je bestaande externe accounts. Of, <a \n"
"href=\"%(signup_url)s\">registreer</a> voor een %(site_name)s account en "
"meld je hiermee aan:"
#: templates/account/login.html:27
msgid "or"
msgstr "of"
#: templates/account/login.html:42
msgid "Forgot Password?"
msgstr "Wachtwoord vergeten?"
#: templates/account/logout.html:5 templates/account/logout.html.py:8
msgid "Signed Out"
msgstr "Afgemeld"
#: templates/account/password_change.html:4
#: templates/account/password_change.html:7
#: templates/account/password_change.html:12
#: templates/account/password_reset_from_key.html:4
#: templates/account/password_reset_from_key.html:7
msgid "Change Password"
msgstr "Wachtwoord wijzigen"
#: templates/account/password_delete.html:5
#: templates/account/password_delete.html:8
msgid "Delete Password"
msgstr "Wis wachtwoord"
#: templates/account/password_delete.html:9
msgid ""
"You may delete your password since you are currently logged in using OpenID."
msgstr "Je kunt je wachtwoord wissen omdat je via OpenID bent ingelogd."
#: templates/account/password_delete.html:12
msgid "delete my password"
msgstr "Wis mijn wachtwoord"
#: templates/account/password_delete_done.html:5
#: templates/account/password_delete_done.html:8
msgid "Password Deleted"
msgstr "Wachtwoord gewist"
#: templates/account/password_delete_done.html:9
msgid "Your password has been deleted."
msgstr "Je wachtwoord is gewist."
#: templates/account/password_reset.html:6
#: templates/account/password_reset.html:10
#: templates/account/password_reset_done.html:6
#: templates/account/password_reset_done.html:9
msgid "Password Reset"
msgstr "Nieuw wachtwoord"
#: templates/account/password_reset.html:15
msgid ""
"Forgotten your password? Enter your e-mail address below, and we'll send you "
"an e-mail allowing you to reset it."
msgstr ""
"Wachtwoord vergeten? Vul je e-mail adres in en we sturen je een e-mail "
"waarmee je een nieuw wachtwoord kunt instellen."
#: templates/account/password_reset.html:20
msgid "Reset My Password"
msgstr "Herstel mijn wachtwoord"
#: templates/account/password_reset.html:23
#, python-format
msgid ""
"If you have any trouble resetting your password, contact us at <a href="
"\"mailto:%(CONTACT_EMAIL)s\">%(CONTACT_EMAIL)s</a>."
msgstr ""
"Als je problemen hebt je wachtwoord opnieuw in te stellen, neem dan contact "
"op met <a href=\"mailto:%(CONTACT_EMAIL)s\">%(CONTACT_EMAIL)s</a>."
#: templates/account/password_reset_done.html:15
#, python-format
msgid ""
"We have sent you an e-mail. If you do not receive it within a few minutes, "
"contact us at <a href=\"mailto:%(CONTACT_EMAIL)s\">%(CONTACT_EMAIL)s</a>."
msgstr ""
"We hebben je een e-mail verstuurd. Als je deze niet binnen enkele minuten "
"ontvangen hebt, neem dan contact op met <a href=\"mailto:%(CONTACT_EMAIL)s\">"
"%(CONTACT_EMAIL)s</a>."
#: templates/account/password_reset_from_key.html:7
msgid "Bad Token"
msgstr "Ongeldige sleutel"
#: templates/account/password_reset_from_key.html:11
#, python-format
msgid ""
"The password reset link was invalid, possibly because it has already been "
"used. Please request a <a href=\"%(passwd_reset_url)s\">new password reset</"
"a>."
msgstr ""
"De link om je wachtwoord opnieuw in te stellen is niet geldig. Mogelijk is "
"deze al een keer gebruikt. <a href=\"%(passwd_reset_url)s\">Herstel je "
"wachtwoord</a> opnieuw."
#: templates/account/password_reset_from_key.html:17
msgid "change password"
msgstr "Wachtwoord wijzigen"
#: templates/account/password_reset_from_key.html:20
msgid "Your password is now changed."
msgstr "Je wachtwoord is gewijzigd."
#: templates/account/password_reset_key_message.txt:1
#, python-format
msgid ""
"You're receiving this e-mail because you or someone else has requested a "
"password for your user account at %(site_domain)s.\n"
"It can be safely ignored if you did not request a password reset. Click the "
"link below to reset your password.\n"
"\n"
"%(password_reset_url)s\n"
"\n"
"In case you forgot, your username is %(username)s.\n"
"\n"
"Thanks for using our site!\n"
msgstr ""
"Je ontvangt deze mail omdat er een verzoek is ingelegd om het wachtwoord "
"behorende bij je %(site_domain)s account opnieuw in te stellen. Je kunt deze "
"gerust mail negeren als je dit niet zelf gedaan hebt. Anders, klik op de "
"volgende link om je wachtwoord opnieuw in te stellen.\n"
"\n"
"%(password_reset_url)s\n"
"\n"
"Deze link hoort bij je account met gebruikersnaam %(username)s.\n"
"\n"
"Bedankt voor het gebruik van onze site!\n"
#: templates/account/password_set.html:5 templates/account/password_set.html:8
#: templates/account/password_set.html:13
msgid "Set Password"
msgstr "Zet wachtwoord"
#: templates/account/signup.html:5 templates/socialaccount/signup.html:5
msgid "Signup"
msgstr "Registreren"
#: templates/account/signup.html:8 templates/account/signup.html.py:21
#: templates/socialaccount/signup.html:8
#: templates/socialaccount/signup.html:19
msgid "Sign Up"
msgstr "Registreren"
#: templates/account/signup.html:13
#, python-format
msgid ""
"Already have an account? Then please <a href=\"%(login_url)s\">sign in</a>."
msgstr "Heb je al een account? <a href=\"%(login_url)s\">Meld je dan aan</a>."
#: templates/account/verification_sent.html:5
#: templates/account/verification_sent.html:8
#: templates/account/verified_email_required.html:5
#: templates/account/verified_email_required.html:8
msgid "Verify Your E-mail Address"
msgstr "Verifieer je e-mail adres"
#: templates/account/verification_sent.html:10
#, python-format
msgid ""
"We have sent you an e-mail to <b>%(email)s</b> for verification. Follow the "
"link provided to finalize the signup process. If you do not receive it "
"within a few minutes, contact us at <a href=\"mailto:%(CONTACT_EMAIL)s\">"
"%(CONTACT_EMAIL)s</a>."
msgstr ""
"We hebben een e-mail verstuurd aan <b>%(email)s</b> ter verificatie. Volg de "
"link in deze mail om je registratie af te sluiten. Als je de e-mail niet "
"binnen enkele minuten ontvangt, neem dan contact op met <a href=\"mailto:"
"%(CONTACT_EMAIL)s\">%(CONTACT_EMAIL)s</a>."
#: templates/account/verified_email_required.html:12
msgid ""
"This part of the site requires us to verify that\n"
"you are who you claim to be. For this purpose, we require that you\n"
"verify ownership of your e-mail address. "
msgstr ""
"Voor dit gedeelte van de site is het nodig dat we je identiteit\n"
"verifiëren. Via een verificatie e-mail kunnen we controleren dat je\n"
"daadwerkelijk toegang hebt tot het opgegeven e-mail adres."
#: templates/account/verified_email_required.html:16
#, python-format
msgid ""
"We have sent an e-mail to you for\n"
"verification. Please click on the link inside this\n"
"e-mail. If you do not receive it within a few minutes, contact us\n"
"at <a href=\"mailto:%(CONTACT_EMAIL)s\">%(CONTACT_EMAIL)s</a>. \n"
msgstr ""
"Volg de link in de verificatie e-mail om de controle af te ronden. Als je de\n"
"e-mail niet binnen enkele minuten ontvangt, neem dan contact op met <a\n"
"href=\"mailto:%(CONTACT_EMAIL)s\">%(CONTACT_EMAIL)s</a>. \n"
#: templates/account/verified_email_required.html:22
#, python-format
msgid ""
"<strong>Note:</strong> you can still <a href=\"%(email_url)s\">change your e-"
"mail address</a>."
msgstr "<strong>Merk op:</strong> je kunt altijd <a href=\"%(email_url)s\">je e-mail adres wijzigen</a>."
#: templates/account/snippets/already_logged_in.html:5
msgid "Note"
msgstr "Notitie"
#: templates/account/snippets/already_logged_in.html:5
#, python-format
msgid "you are already logged in as %(user_display)s."
msgstr "je bent al ingelogd als %(user_display)s."
#: templates/emailconfirmation/confirm_email.html:6
#: templates/emailconfirmation/confirm_email.html:12
msgid "E-mail Address Confirmation"
msgstr "E-mail adres bevestiging"
#: templates/emailconfirmation/confirm_email.html:16
#, python-format
msgid ""
"You have confirmed that <a href=\"mailto:%(email)s\">%(email)s</a> is an e-"
"mail address for user %(user_display)s."
msgstr ""
"Je hebt bevestigd dat <a href=\"mailto:%(email)s\">%(email)s</a> een e-mail "
"adres is voor gebruiker %(user_display)s."
#: templates/emailconfirmation/confirm_email.html:18
msgid "Invalid confirmation key."
msgstr "Ongeldige bevestigingssleutel."
#: templates/emailconfirmation/email_confirmation_message.txt:1
#, python-format
msgid ""
"User %(user_display)s at %(site_name)s has given this as an email address.\n"
"\n"
"To confirm this is correct, go to %(activate_url)s\n"
msgstr ""
"Gebruiker %(user_display)s van %(site_name)s heeft dit als e-mail adres "
"opgegeven.\n"
"\n"
"Bezoek de volgende link en bevestig dat dit correct is: %(activate_url)s\n"
#: templates/emailconfirmation/email_confirmation_subject.txt:1
msgid "Confirm E-mail Address"
msgstr "Bevestig e-mail adres"
#: templates/openid/login.html:9
msgid "OpenID Sign In"
msgstr "Aanmelden via OpenID"
#: templates/socialaccount/account_inactive.html:5
#: templates/socialaccount/account_inactive.html:8
msgid "Account Inactive"
msgstr "Account inactief"
#: templates/socialaccount/account_inactive.html:10
msgid "This account is inactive."
msgstr "Dit account is niet actief"
#: templates/socialaccount/authentication_error.html:5
#: templates/socialaccount/authentication_error.html:8
msgid "Social Network Login Failure"
msgstr "Aanmelden Mislukt"
#: templates/socialaccount/authentication_error.html:10
msgid ""
"An error occured while attempting to login via your social network account."
msgstr ""
"Er is een fout opgetreden toen we je wilde inloggen via je externe account."
#: templates/socialaccount/connections.html:5
#: templates/socialaccount/connections.html:8
msgid "Account Connections"
msgstr "Account Connecties"
#: templates/socialaccount/connections.html:11
msgid ""
"You can sign in to your account using any of the following third party "
"accounts:"
msgstr "Je kunt jezelf aanmelden met een van de volgende externe accounts:"
#: templates/socialaccount/connections.html:46
msgid "Add a 3rd Party Account"
msgstr "Voeg een extern account toe"
#: templates/socialaccount/login_cancelled.html:5
#: templates/socialaccount/login_cancelled.html:9
msgid "Login Cancelled"
msgstr "Aanmelden geannuleerd"
#: templates/socialaccount/login_cancelled.html:13
#, python-format
msgid ""
"You decided to cancel logging in to our site using one of your exisiting "
"accounts. If this was a mistake, please proceed to <a href=\"%(login_url)s"
"\">sign in</a>."
msgstr ""
"Je hebt het aanmelden via een extern account geannuleerd. Als dit een "
"vergissing was, <a href=\"%(login_url)s\">meld je dan opnieuw aan</a>."
#: templates/socialaccount/signup.html:10
#, python-format
msgid ""
"You are about to use your %(provider_name)s account to login to \n"
"%(site_name)s. As a final step, please complete the following form:"
msgstr ""
"Om bij %(site_name)s in te kunnen loggen via %(provider_name)s hebben we de "
"volgende gegevens nodig:"
#~ msgid "OpenID"
#~ msgstr "OpenID"
#~ msgid "Already have an account?"
#~ msgstr "Heb je al een account?"
#~ msgid "Sign in"
#~ msgstr "Aanmelden"
#~ msgid "Language"
#~ msgstr "Taal"
#~ msgid "Pinax can be used in your preferred language."
#~ msgstr "Deze site kan in jouw voorkeurstaal gebruikt worden."
#~ msgid "Change my language"
#~ msgstr "Verander mijn taal"
#~ msgid "Timezone"
#~ msgstr "Tijdzone"
#, fuzzy
#~ msgid ""
#~ "You're receiving this e-mail because you requested a password reset\n"
#~ "for your user account at Pinax.\n"
#~ "\n"
#~ "Your new password is: %(new_password)s\n"
#~ "\n"
#~ "Your username, in case you've forgotten: %(username)s\n"
#~ "\n"
#~ "You should log in as soon as possible and change your password.\n"
#~ "\n"
#~ "Thanks for using our site!\n"
#~ msgstr ""
#~ "Je ontvangt deze mail omdat er een verzoek is ingelegd om het wachtwoord\n"
#~ "behorende bij je %(site_name)s account opnieuw in te stellen.\n"
#~ "\n"
#~ "Je nieuwe wachtwoord is: %(new_password)s\n"
#~ "\n"
#~ "Je gebruikersnaam, voor het geval je die vergeten bent, is: %(username)s\n"
#~ "\n"
#~ "Je moet zo snel mogelijk inloggen en bovenstaand wachtwoord veranderen.\n"
#~ "\n"
#~ "Bedankt voor het gebruik van onze site!\n"
#~ msgid "If checked you will stay logged in for 3 weeks"
#~ msgstr "Bij 'Onthouden' blijf je ingelogd gedurende 3 weken"
#~ msgid "Timezone successfully updated."
#~ msgstr "Tijdzone gewijzigd."
#~ msgid "Language successfully updated."
#~ msgstr "Taal gewijzigd."
#~ msgid "E-Mail Addresses"
#~ msgstr "E-mail adressen"
#~ msgid "None"
#~ msgstr "Geen"
#~ msgid "add"
#~ msgstr "Voeg toe"
#~ msgid "Log In"
#~ msgstr "Inloggen"
#~ msgid "Log in"
#~ msgstr "Inloggen"
#~ msgid "Logout"
#~ msgstr "Afmelden"
#~ msgid ""
#~ "When you receive the new password, you should <a href=\"%(login_url)s"
#~ "\">log in</a> and change it as soon as possible."
#~ msgstr ""
#~ "Zodra je het nieuwe wachtwoord ontvangen hebt moet je zo snel mogelijk <a "
#~ "href=\"%(login_url)s\">inloggen</a> en het wachtwoord wijzigen."
#~ msgid "You are already logged in."
#~ msgstr "Je bent al ingelogd."
#~ msgid ""
#~ "By clicking \"Sign Up\", you are indicating that you have read and agree "
#~ "to the <a href=\"%(terms_url)s\">Terms of Use</a> and <a href="
#~ "\"%(privacy_url)s\">Privacy Policy</a>."
#~ msgstr ""
#~ "Door te registreren geef je aan dat je de <a href=\"%(terms_url)s"
#~ "\">gebruiksvoorwaarden</a> en de <a href=\"%(privacy_url)s\">privacy "
#~ "policy</a> gelezen hebt en ermee akkoord gaat."
#~ msgid ""
#~ "If you have any trouble creating your account, contact us at <a href="
#~ "\"mailto:%(contact_email)s\">%(contact_email)s</a>."
#~ msgstr ""
#~ "Als je problemen hebt om een account aan te maken, neem dan contact op "
#~ "met <a href=\"mailto:%(contact_email)s\">%(contact_email)s</a>."
#~ msgid "Log in &raquo;"
#~ msgstr "Inloggen"

0
itf/allauth/models.py Normal file
View File

View File

View File

@ -0,0 +1,21 @@
from django.contrib import admin
from models import SocialApp, SocialAccount, SocialToken
class SocialAppAdmin(admin.ModelAdmin):
list_display = ('name', 'provider', 'site')
class SocialAccountAdmin(admin.ModelAdmin):
raw_id_fields = ('user',)
list_display = ('user', 'uid', 'provider')
class SocialTokenAdmin(admin.ModelAdmin):
raw_id_fields = ('app', 'account',)
list_display = ('app', 'account', 'token')
admin.site.register(SocialApp, SocialAppAdmin)
admin.site.register(SocialToken, SocialTokenAdmin)
admin.site.register(SocialAccount, SocialAccountAdmin)

View File

@ -0,0 +1,21 @@
from django.conf import settings
from allauth.account import app_settings as account_settings
# Request e-mail address from 3rd party account provider? E.g. OpenID AX
QUERY_EMAIL = getattr(settings, "SOCIALACCOUNT_QUERY_EMAIL",
account_settings.EMAIL_REQUIRED)
# Attempt to bypass the signup form by using fields (e.g. username,
# email) retrieved from the social account provider. If a conflict
# arises due to a duplicate e-mail signup form will still kick in.
AUTO_SIGNUP = getattr(settings, "SOCIALACCOUNT_AUTO_SIGNUP", True)
# Enable support for django-avatar. When enabled, the profile image of
# the user is copied locally into django-avatar at signup.
AVATAR_SUPPORT = getattr(settings, "SOCIALACCOUNT_AVATAR_SUPPORT",
'avatar' in settings.INSTALLED_APPS)
# Provider specific settings
PROVIDERS = getattr(settings, "SOCIALACCOUNT_PROVIDERS", {})

View File

@ -0,0 +1,5 @@
import providers
def socialaccount(request):
ctx = { 'providers': providers.registry.get_list() }
return dict(socialaccount=ctx)

View File

@ -0,0 +1,60 @@
# Courtesy of django-social-auth
from django.core.exceptions import ValidationError
from django.db import models
from django.utils import simplejson
from django.utils.encoding import smart_unicode
class JSONField(models.TextField):
"""Simple JSON field that stores python structures as JSON strings
on database.
"""
__metaclass__ = models.SubfieldBase
def to_python(self, value):
"""
Convert the input JSON value into python structures, raises
django.core.exceptions.ValidationError if the data can't be converted.
"""
if self.blank and not value:
return None
if isinstance(value, basestring):
try:
return simplejson.loads(value)
except Exception, e:
raise ValidationError(str(e))
else:
return value
def validate(self, value, model_instance):
"""Check value is a valid JSON string, raise ValidationError on
error."""
if isinstance(value, basestring):
super(JSONField, self).validate(value, model_instance)
try:
simplejson.loads(value)
except Exception, e:
raise ValidationError(str(e))
def get_prep_value(self, value):
"""Convert value to JSON string before save"""
try:
return simplejson.dumps(value)
except Exception, e:
raise ValidationError(str(e))
def value_to_string(self, obj):
"""Return value from object converted to string properly"""
return smart_unicode(self.get_prep_value(self._get_val_from_obj(obj)))
def value_from_object(self, obj):
"""Return value dumped to string."""
return self.get_prep_value(self._get_val_from_obj(obj))
try:
from south.modelsinspector import add_introspection_rules
add_introspection_rules([], ["^allauth\.socialaccount\.fields\.JSONField"])
except:
pass

View File

@ -0,0 +1,55 @@
from django.utils.translation import ugettext_lazy as _
from django import forms
from emailconfirmation.models import EmailAddress
from models import SocialAccount
from allauth.account.forms import BaseSignupForm
from allauth.account.utils import send_email_confirmation
class SignupForm(BaseSignupForm):
def __init__(self, *args, **kwargs):
self.sociallogin = kwargs.pop('sociallogin')
user = self.sociallogin.account.user
initial = { 'email': user.email or '',
'username': user.username or '',
'first_name': user.first_name or '',
'last_name': user.last_name or '' }
kwargs['initial'] = initial
super(SignupForm, self).__init__(*args, **kwargs)
def save(self, request=None):
new_user = self.create_user()
self.sociallogin.account.user = new_user
self.sociallogin.save()
super(SignupForm, self).save(new_user)
# Confirmation last (save may alter first_name etc -- used in mail)
send_email_confirmation(new_user, request=request)
return new_user
class DisconnectForm(forms.Form):
account = forms.ModelChoiceField(queryset=SocialAccount.objects.none(),
widget=forms.RadioSelect,
required=True)
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user')
self.accounts = SocialAccount.objects.filter(user=self.user)
super(DisconnectForm, self).__init__(*args, **kwargs)
self.fields['account'].queryset = self.accounts
def clean(self):
if len(self.accounts) == 1:
# No usable password would render the local account unusable
if not self.user.has_usable_password():
raise forms.ValidationError(_("Your local account has no password setup."))
# No email address, no password reset
if EmailAddress.objects.filter(user=self.user,
verified=True).count() == 0:
raise forms.ValidationError(_("Your local account has no verified e-mail address."))
return self.cleaned_data
def save(self):
self.cleaned_data['account'].delete()

View File

@ -0,0 +1,179 @@
from django.contrib import messages
from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect
from django.template import RequestContext
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
from django.template.defaultfilters import slugify
from allauth.utils import get_login_redirect_url, \
generate_unique_username, email_address_exists
from allauth.account.utils import send_email_confirmation, \
perform_login, complete_signup
from allauth.account import app_settings as account_settings
import app_settings
def _process_signup(request, sociallogin):
# If email is specified, check for duplicate and if so, no auto signup.
auto_signup = app_settings.AUTO_SIGNUP
email = sociallogin.account.user.email
if auto_signup:
# Let's check if auto_signup is really possible...
if email:
if account_settings.UNIQUE_EMAIL:
if email_address_exists(email):
# Oops, another user already has this address. We
# cannot simply connect this social account to the
# existing user. Reason is that the email adress may
# not be verified, meaning, the user may be a hacker
# that has added your email address to his account in
# the hope that you fall in his trap. We cannot check
# on 'email_address.verified' either, because
# 'email_address' is not guaranteed to be verified.
auto_signup = False
# FIXME: We redirect to signup form -- user will
# see email address conflict only after posting
# whereas we detected it here already.
elif account_settings.EMAIL_REQUIRED:
# Nope, email is required and we don't have it yet...
auto_signup = False
if not auto_signup:
request.session['socialaccount_sociallogin'] = sociallogin
url = reverse('socialaccount_signup')
ret = HttpResponseRedirect(url)
else:
# FIXME: There is some duplication of logic inhere
# (create user, send email, in active etc..)
u = sociallogin.account.user
u.username = generate_unique_username(u.username
or email
or 'user')
u.last_name = (u.last_name or '') \
[0:User._meta.get_field('last_name').max_length]
u.first_name = (u.first_name or '') \
[0:User._meta.get_field('first_name').max_length]
u.email = email or ''
u.set_unusable_password()
sociallogin.save()
send_email_confirmation(u, request=request)
ret = complete_social_signup(request, sociallogin)
return ret
def _login_social_account(request, sociallogin):
user = sociallogin.account.user
if not user.is_active:
ret = render_to_response(
'socialaccount/account_inactive.html',
{},
context_instance=RequestContext(request))
else:
ret = perform_login(request, user,
redirect_url=sociallogin.get_redirect_url())
return ret
def render_authentication_error(request, extra_context={}):
return render_to_response(
"socialaccount/authentication_error.html",
extra_context, context_instance=RequestContext(request))
def complete_social_login(request, sociallogin):
assert not sociallogin.is_existing
sociallogin.lookup()
if request.user.is_authenticated():
if sociallogin.is_existing:
# Existing social account, existing user
if sociallogin.account.user != request.user:
# Social account of other user. Simply logging in may
# not be correct in the case that the user was
# attempting to hook up another social account to his
# existing user account. For now, this scenario is not
# supported. Issue is that one cannot simply remove
# the social account from the other user, as that may
# render the account unusable.
pass
ret = _login_social_account(request, sociallogin)
else:
# New social account
sociallogin.account.user = request.user
sociallogin.save()
default_next = reverse('socialaccount_connections')
next = sociallogin.get_redirect_url(fallback=default_next)
messages.add_message(request, messages.INFO,
_('The social account has been connected'
' to your existing account'))
return HttpResponseRedirect(next)
else:
if sociallogin.is_existing:
# Login existing user
ret = _login_social_account(request, sociallogin)
else:
# New social user
ret = _process_signup(request, sociallogin)
return ret
def _name_from_url(url):
"""
>>> _name_from_url('http://google.com/dir/file.ext')
u'file.ext'
>>> _name_from_url('http://google.com/dir/')
u'dir'
>>> _name_from_url('http://google.com/dir')
u'dir'
>>> _name_from_url('http://google.com/dir/..')
u'dir'
>>> _name_from_url('http://google.com/dir/../')
u'dir'
>>> _name_from_url('http://google.com')
u'google.com'
>>> _name_from_url('http://google.com/dir/subdir/file..ext')
u'file.ext'
"""
from urlparse import urlparse
p = urlparse(url)
for base in (p.path.split('/')[-1],
p.path,
p.netloc):
name = ".".join(filter(lambda s: s,
map(slugify, base.split("."))))
if name:
return name
def _copy_avatar(request, user, account):
import urllib2
from django.core.files.base import ContentFile
from avatar.models import Avatar
url = account.get_avatar_url()
if url:
ava = Avatar(user=user)
ava.primary = Avatar.objects.filter(user=user).count() == 0
try:
content = urllib2.urlopen(url).read()
name = _name_from_url(url)
ava.avatar.save(name, ContentFile(content))
except IOError:
# Let's nog make a big deal out of this...
pass
def complete_social_signup(request, sociallogin):
if app_settings.AVATAR_SUPPORT:
_copy_avatar(request, sociallogin.account.user, sociallogin.account)
return complete_signup(request,
sociallogin.account.user,
sociallogin.get_redirect_url())
# TODO: Factor out callable importing functionality
# See: account.utils.user_display
def import_path(path):
modname, _, attr = path.rpartition('.')
m = __import__(modname, fromlist=[attr])
return getattr(m, attr)

View File

@ -0,0 +1,73 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'SocialAccount'
db.create_table('socialaccount_socialaccount', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
('last_login', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)),
('date_joined', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)),
))
db.send_create_signal('socialaccount', ['SocialAccount'])
def backwards(self, orm):
# Deleting model 'SocialAccount'
db.delete_table('socialaccount_socialaccount')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'socialaccount.socialaccount': {
'Meta': {'object_name': 'SocialAccount'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
}
}
complete_apps = ['socialaccount']

View File

@ -0,0 +1,150 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'SocialToken'
db.create_table('socialaccount_socialtoken', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('app', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['socialaccount.SocialApp'])),
('account', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['socialaccount.SocialAccount'])),
('token', self.gf('django.db.models.fields.CharField')(max_length=200)),
('token_secret', self.gf('django.db.models.fields.CharField')(max_length=200, blank=True)),
))
db.send_create_signal('socialaccount', ['SocialToken'])
# Adding unique constraint on 'SocialToken', fields ['app', 'account']
db.create_unique('socialaccount_socialtoken', ['app_id', 'account_id'])
# Adding model 'SocialApp'
db.create_table('socialaccount_socialapp', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('site', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['sites.Site'])),
('provider', self.gf('django.db.models.fields.CharField')(max_length=30)),
('name', self.gf('django.db.models.fields.CharField')(max_length=40)),
('key', self.gf('django.db.models.fields.CharField')(max_length=100)),
('secret', self.gf('django.db.models.fields.CharField')(max_length=100)),
))
db.send_create_signal('socialaccount', ['SocialApp'])
# Adding field 'SocialAccount.provider'
db.add_column('socialaccount_socialaccount', 'provider', self.gf('django.db.models.fields.CharField')(default='', max_length=30, blank=True), keep_default=False)
# Adding field 'SocialAccount.uid'
db.add_column('socialaccount_socialaccount', 'uid', self.gf('django.db.models.fields.CharField')(default='', max_length=255, blank=True), keep_default=False)
# Adding field 'SocialAccount.extra_data'
db.add_column('socialaccount_socialaccount', 'extra_data', self.gf('allauth.socialaccount.fields.JSONField')(default='{}'), keep_default=False)
# Changing field 'SocialAccount.last_login'
db.alter_column('socialaccount_socialaccount', 'last_login', self.gf('django.db.models.fields.DateTimeField')(auto_now=True))
# Changing field 'SocialAccount.date_joined'
db.alter_column('socialaccount_socialaccount', 'date_joined', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True))
def backwards(self, orm):
# Removing unique constraint on 'SocialToken', fields ['app', 'account']
db.delete_unique('socialaccount_socialtoken', ['app_id', 'account_id'])
# Deleting model 'SocialToken'
db.delete_table('socialaccount_socialtoken')
# Deleting model 'SocialApp'
db.delete_table('socialaccount_socialapp')
# Deleting field 'SocialAccount.provider'
db.delete_column('socialaccount_socialaccount', 'provider')
# Deleting field 'SocialAccount.uid'
db.delete_column('socialaccount_socialaccount', 'uid')
# Deleting field 'SocialAccount.extra_data'
db.delete_column('socialaccount_socialaccount', 'extra_data')
# Changing field 'SocialAccount.last_login'
db.alter_column('socialaccount_socialaccount', 'last_login', self.gf('django.db.models.fields.DateTimeField')())
# Changing field 'SocialAccount.date_joined'
db.alter_column('socialaccount_socialaccount', 'date_joined', self.gf('django.db.models.fields.DateTimeField')())
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'sites.site': {
'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"},
'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'socialaccount.socialaccount': {
'Meta': {'object_name': 'SocialAccount'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'extra_data': ('allauth.socialaccount.fields.JSONField', [], {'default': "'{}'"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'provider': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'uid': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'socialaccount.socialapp': {
'Meta': {'object_name': 'SocialApp'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'key': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
'provider': ('django.db.models.fields.CharField', [], {'max_length': '30'}),
'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"})
},
'socialaccount.socialtoken': {
'Meta': {'unique_together': "(('app', 'account'),)", 'object_name': 'SocialToken'},
'account': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['socialaccount.SocialAccount']"}),
'app': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['socialaccount.SocialApp']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'token': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'token_secret': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'})
}
}
complete_apps = ['socialaccount']

View File

@ -0,0 +1,96 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
depends_on = (('facebook', '0003_tosocialaccount'),
('twitter', '0003_tosocialaccount'),
('openid', '0002_tosocialaccount'))
def forwards(self, orm):
# Adding unique constraint on 'SocialAccount', fields ['uid', 'provider']
db.create_unique('socialaccount_socialaccount', ['uid', 'provider'])
def backwards(self, orm):
# Removing unique constraint on 'SocialAccount', fields ['uid', 'provider']
db.delete_unique('socialaccount_socialaccount', ['uid', 'provider'])
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'sites.site': {
'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"},
'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'socialaccount.socialaccount': {
'Meta': {'unique_together': "(('provider', 'uid'),)", 'object_name': 'SocialAccount'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'extra_data': ('allauth.socialaccount.fields.JSONField', [], {'default': "'{}'"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'provider': ('django.db.models.fields.CharField', [], {'max_length': '30'}),
'uid': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'socialaccount.socialapp': {
'Meta': {'object_name': 'SocialApp'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'key': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
'provider': ('django.db.models.fields.CharField', [], {'max_length': '30'}),
'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"})
},
'socialaccount.socialtoken': {
'Meta': {'unique_together': "(('app', 'account'),)", 'object_name': 'SocialToken'},
'account': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['socialaccount.SocialAccount']"}),
'app': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['socialaccount.SocialApp']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'token': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'token_secret': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'})
}
}
complete_apps = ['socialaccount']

View File

@ -0,0 +1,197 @@
from django.db import models
from django.contrib.auth import authenticate
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.utils import simplejson
import allauth.app_settings
from allauth.utils import get_login_redirect_url
import providers
from fields import JSONField
class SocialAppManager(models.Manager):
def get_current(self, provider):
site = Site.objects.get_current()
return self.get(site=site,
provider=provider)
class SocialApp(models.Model):
objects = SocialAppManager()
site = models.ForeignKey(Site)
provider = models.CharField(max_length=30,
choices=providers.registry.as_choices())
name = models.CharField(max_length=40)
key = models.CharField(max_length=100,
help_text='App ID, or consumer key')
secret = models.CharField(max_length=100,
help_text='API secret, or consumer secret')
def __unicode__(self):
return self.name
class SocialAccount(models.Model):
user = models.ForeignKey(User)
provider = models.CharField(max_length=30,
choices=providers.registry.as_choices())
# Just in case you're wondering if an OpenID identity URL is going
# to fit in a 'uid':
#
# Ideally, URLField(max_length=1024, unique=True) would be used
# for identity. However, MySQL has a max_length limitation of 255
# for URLField. How about models.TextField(unique=True) then?
# Well, that won't work either for MySQL due to another bug[1]. So
# the only way out would be to drop the unique constraint, or
# switch to shorter identity URLs. Opted for the latter, as [2]
# suggests that identity URLs are supposed to be short anyway, at
# least for the old spec.
#
# [1] http://code.djangoproject.com/ticket/2495.
# [2] http://openid.net/specs/openid-authentication-1_1.html#limits
uid = models.CharField(max_length=255)
last_login = models.DateTimeField(auto_now=True)
date_joined = models.DateTimeField(auto_now_add=True)
extra_data = JSONField(default='{}')
class Meta:
unique_together = ('provider', 'uid')
def authenticate(self):
return authenticate(account=self)
def __unicode__(self):
return unicode(self.user)
def get_profile_url(self):
return self.get_provider_account().get_profile_url()
def get_avatar_url(self):
return self.get_provider_account().get_avatar_url()
def get_provider(self):
return providers.registry.by_id(self.provider)
def get_provider_account(self):
return self.get_provider().wrap_account(self)
class SocialToken(models.Model):
app = models.ForeignKey(SocialApp)
account = models.ForeignKey(SocialAccount)
token = models.CharField(max_length=200)
token_secret = models.CharField(max_length=200, blank=True)
class Meta:
unique_together = ('app', 'account')
def __unicode__(self):
return self.token
class SocialLogin(object):
"""
Represents a social user that is in the process of being logged
in. This consists of the following information:
`account` (`SocialAccount` instance): The social account being
logged in. Providers are not responsible for checking whether or
not an account already exists or not. Therefore, a provider
typically creates a new (unsaved) `SocialAccount` instance. The
`User` instance pointed to by the account (`account.user`) may be
prefilled by the provider for use as a starting point later on
during the signup process.
`token` (`SocialToken` instance): An optional access token token
that results from performing a successful authentication
handshake.
`state` (`dict`): The state to be preserved during the
authentication handshake. Note that this state may end up in the
url (e.g. OAuth2 `state` parameter) -- do not put any secrets in
there. It currently only contains the url to redirect to after
login.
"""
def __init__(self, account, token=None):
if token:
assert token.account is None or token.account == account
token.account = account
self.token = token
self.account = account
self.state = {}
def save(self):
user = self.account.user
user.save()
self.account.user = user
self.account.save()
if self.token:
self.token.account = self.account
self.token.save()
@property
def is_existing(self):
"""
Account is temporary, not yet backed by a database record.
"""
return self.account.pk
def lookup(self):
"""
Lookup existing account, if any.
"""
assert not self.is_existing
try:
a = SocialAccount.objects.get(provider=self.account.provider,
uid=self.account.uid)
# Update account
a.extra_data = self.account.extra_data
self.account = a
a.save()
# Update token
if self.token:
assert not self.token.pk
try:
t = SocialToken.objects.get(account=self.account,
app=self.token.app)
t.token = self.token.token
t.token_secret = self.token.token_secret
t.save()
self.token = t
except SocialToken.DoesNotExist:
self.token.account = a
self.token.save()
except SocialAccount.DoesNotExist:
pass
def get_redirect_url(self,
fallback=allauth.app_settings.LOGIN_REDIRECT_URL):
url = self.state.get('next') or fallback
return url
@classmethod
def state_from_request(cls, request):
state = {}
next = get_login_redirect_url(request, fallback=None)
if next:
state['next'] = next
return state
@classmethod
def marshall_state(cls, request):
state = cls.state_from_request(request)
return simplejson.dumps(state)
@classmethod
def unmarshall_state(cls, state_string):
if state_string:
state = simplejson.loads(state_string)
else:
state = {}
return state

View File

@ -0,0 +1,36 @@
from django.conf import settings
from django.utils import importlib
class ProviderRegistry(object):
def __init__(self):
self.provider_map = {}
self.loaded = False
def get_list(self):
self.load()
return self.provider_map.values()
def register(self, cls):
self.load()
self.provider_map[cls.id] = cls()
def by_id(self, id):
self.load()
return self.provider_map[id]
def as_choices(self):
self.load()
for provider in self.get_list():
yield (provider.id, provider.name)
def load(self):
if not self.loaded:
for app in settings.INSTALLED_APPS:
provider_module = app + '.provider'
try:
importlib.import_module(provider_module)
except ImportError:
pass
self.loaded = True
registry = ProviderRegistry()

View File

@ -0,0 +1,52 @@
from allauth.socialaccount import app_settings
from allauth.socialaccount.models import SocialApp
class Provider(object):
def get_login_url(self, request, next=None, **kwargs):
"""
Builds the URL to redirect to when initiating a login for this
provider.
"""
raise NotImplementedError, "get_login_url() for " + self.name
def get_app(self, request):
return SocialApp.objects.get_current(self.id)
def media_js(self, request):
"""
Some providers may require extra scripts (e.g. a Facebook connect)
"""
return ''
def wrap_account(self, social_account):
return self.account_class(social_account)
def get_settings(self):
return app_settings.PROVIDERS.get(self.id, {})
class ProviderAccount(object):
def __init__(self, social_account):
self.account = social_account
def get_profile_url(self):
return None
def get_avatar_url(self):
return None
def get_brand(self):
"""
Returns a dict containing an id and name identifying the
brand. Useful when displaying logos next to accounts in
templates.
For most providers, these are identical to the provider. For
OpenID however, the brand can derived from the OpenID identity
url.
"""
provider = self.account.get_provider()
return dict(id=provider.id,
name=provider.name)
def __unicode__(self):
return self.get_brand()['name']

View File

@ -0,0 +1,850 @@
<?xml version="1.0" encoding="UTF-8"?>
<locales>
<locale>
<englishName>Afrikaans</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>af_ZA</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Arabic</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>ar_AR</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Azerbaijani</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>az_AZ</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Belarusian</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>be_BY</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Bulgarian</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>bg_BG</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Bengali</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>bn_IN</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Bosnian</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>bs_BA</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Catalan</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>ca_ES</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Czech</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>cs_CZ</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Welsh</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>cy_GB</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Danish</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>da_DK</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>German</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>de_DE</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Greek</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>el_GR</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>English (UK)</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>en_GB</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>English (Pirate)</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>en_PI</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>English (Upside Down)</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>en_UD</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>English (US)</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>en_US</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Esperanto</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>eo_EO</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Spanish (Spain)</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>es_ES</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Spanish</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>es_LA</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Estonian</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>et_EE</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Basque</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>eu_ES</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Persian</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>fa_IR</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Leet Speak</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>fb_LT</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Finnish</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>fi_FI</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Faroese</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>fo_FO</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>French (Canada)</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>fr_CA</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>French (France)</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>fr_FR</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Frisian</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>fy_NL</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Irish</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>ga_IE</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Galician</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>gl_ES</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Hebrew</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>he_IL</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Hindi</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>hi_IN</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Croatian</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>hr_HR</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Hungarian</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>hu_HU</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Armenian</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>hy_AM</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Indonesian</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>id_ID</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Icelandic</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>is_IS</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Italian</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>it_IT</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Japanese</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>ja_JP</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Georgian</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>ka_GE</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Khmer</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>km_KH</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Korean</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>ko_KR</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Kurdish</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>ku_TR</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Latin</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>la_VA</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Lithuanian</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>lt_LT</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Latvian</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>lv_LV</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Macedonian</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>mk_MK</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Malayalam</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>ml_IN</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Malay</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>ms_MY</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Norwegian (bokmal)</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>nb_NO</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Nepali</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>ne_NP</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Dutch</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>nl_NL</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Norwegian (nynorsk)</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>nn_NO</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Punjabi</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>pa_IN</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Polish</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>pl_PL</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Pashto</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>ps_AF</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Portuguese (Brazil)</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>pt_BR</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Portuguese (Portugal)</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>pt_PT</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Romanian</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>ro_RO</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Russian</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>ru_RU</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Slovak</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>sk_SK</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Slovenian</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>sl_SI</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Albanian</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>sq_AL</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Serbian</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>sr_RS</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Swedish</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>sv_SE</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Swahili</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>sw_KE</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Tamil</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>ta_IN</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Telugu</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>te_IN</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Thai</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>th_TH</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Filipino</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>tl_PH</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Turkish</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>tr_TR</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Ukrainian</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>uk_UA</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Vietnamese</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>vi_VN</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Simplified Chinese (China)</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>zh_CN</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Traditional Chinese (Hong Kong)</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>zh_HK</representation>
</standard>
</code>
</codes>
</locale>
<locale>
<englishName>Traditional Chinese (Taiwan)</englishName>
<codes>
<code>
<standard>
<name>FB</name>
<representation>zh_TW</representation>
</standard>
</code>
</codes>
</locale>
</locales>

View File

@ -0,0 +1,5 @@
from django import forms
class FacebookConnectForm(forms.Form):
access_token = forms.CharField(required=True)

View File

@ -0,0 +1,70 @@
# Default locale mapping for the Facebook JS SDK
# The list of supported locales is at
# https://www.facebook.com/translations/FacebookLocales.xml
import os
from django.utils.translation import get_language, to_locale
def _build_locale_table(filename_or_file):
"""
Parses the FacebookLocales.xml file and builds a dict relating every
available language ('en, 'es, 'zh', ...) with a list of available regions
for that language ('en' -> 'US', 'EN') and an (arbitrary) default region.
"""
# Require the XML parser module only if we want the default mapping
from xml.dom.minidom import parse
dom = parse(filename_or_file)
reps = dom.getElementsByTagName('representation')
locs = map(lambda r: r.childNodes[0].data, reps)
locale_map = {}
for loc in locs:
lang, _, reg = loc.partition('_')
lang_map = locale_map.setdefault(lang, {'regs': [], 'default': reg})
lang_map['regs'].append(reg)
# Default region overrides (arbitrary)
locale_map['en']['default'] = 'US'
# Special case: Use es_ES for Spain and es_LA for everything else
locale_map['es']['default'] = 'LA'
locale_map['zh']['default'] = 'CN'
locale_map['fr']['default'] = 'FR'
locale_map['pt']['default'] = 'PT'
return locale_map
def get_default_locale_callable():
"""
Wrapper function so that the default mapping is only built when needed
"""
exec_dir = os.path.dirname(os.path.realpath(__file__))
xml_path = os.path.join(exec_dir, 'data', 'FacebookLocales.xml')
fb_locales = _build_locale_table(xml_path)
def default_locale(request):
"""
Guess an appropiate FB locale based on the active Django locale.
If the active locale is available, it is returned. Otherwise,
it tries to return another locale with the same language. If there
isn't one avaible, 'en_US' is returned.
"""
locale = to_locale(get_language())
lang, _, reg = locale.partition('_')
lang_map = fb_locales.get(lang)
if lang_map is not None:
if reg in lang_map['regs']:
chosen = lang + '_' + reg
else:
chosen = lang + '_' + lang_map['default']
else:
chosen = 'en_US'
return chosen
return default_locale

View File

@ -0,0 +1,110 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
depends_on = (('socialaccount', '0001_initial'),)
def forwards(self, orm):
# Adding model 'FacebookApp'
db.create_table('facebook_facebookapp', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('site', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['sites.Site'])),
('name', self.gf('django.db.models.fields.CharField')(max_length=40)),
('application_id', self.gf('django.db.models.fields.CharField')(max_length=80)),
('api_key', self.gf('django.db.models.fields.CharField')(max_length=80)),
('application_secret', self.gf('django.db.models.fields.CharField')(max_length=80)),
))
db.send_create_signal('facebook', ['FacebookApp'])
# Adding model 'FacebookAccount'
db.create_table('facebook_facebookaccount', (
('socialaccount_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['socialaccount.SocialAccount'], unique=True, primary_key=True)),
('social_id', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255)),
('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
('link', self.gf('django.db.models.fields.URLField')(max_length=200)),
))
db.send_create_signal('facebook', ['FacebookAccount'])
def backwards(self, orm):
# Deleting model 'FacebookApp'
db.delete_table('facebook_facebookapp')
# Deleting model 'FacebookAccount'
db.delete_table('facebook_facebookaccount')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'facebook.facebookaccount': {
'Meta': {'object_name': 'FacebookAccount', '_ormbases': ['socialaccount.SocialAccount']},
'link': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'social_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
'socialaccount_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['socialaccount.SocialAccount']", 'unique': 'True', 'primary_key': 'True'})
},
'facebook.facebookapp': {
'Meta': {'object_name': 'FacebookApp'},
'api_key': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
'application_id': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
'application_secret': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"})
},
'sites.site': {
'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"},
'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'socialaccount.socialaccount': {
'Meta': {'object_name': 'SocialAccount'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
}
}
complete_apps = ['facebook']

View File

@ -0,0 +1,108 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'FacebookAccessToken'
db.create_table('facebook_facebookaccesstoken', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('app', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['facebook.FacebookApp'])),
('account', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['facebook.FacebookAccount'])),
('access_token', self.gf('django.db.models.fields.CharField')(max_length=200)),
))
db.send_create_signal('facebook', ['FacebookAccessToken'])
# Adding unique constraint on 'FacebookAccessToken', fields ['app', 'account']
db.create_unique('facebook_facebookaccesstoken', ['app_id', 'account_id'])
def backwards(self, orm):
# Removing unique constraint on 'FacebookAccessToken', fields ['app', 'account']
db.delete_unique('facebook_facebookaccesstoken', ['app_id', 'account_id'])
# Deleting model 'FacebookAccessToken'
db.delete_table('facebook_facebookaccesstoken')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'facebook.facebookaccesstoken': {
'Meta': {'unique_together': "(('app', 'account'),)", 'object_name': 'FacebookAccessToken'},
'access_token': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'account': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['facebook.FacebookAccount']"}),
'app': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['facebook.FacebookApp']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'facebook.facebookaccount': {
'Meta': {'object_name': 'FacebookAccount', '_ormbases': ['socialaccount.SocialAccount']},
'link': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'social_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
'socialaccount_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['socialaccount.SocialAccount']", 'unique': 'True', 'primary_key': 'True'})
},
'facebook.facebookapp': {
'Meta': {'object_name': 'FacebookApp'},
'api_key': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
'application_id': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
'application_secret': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"})
},
'sites.site': {
'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"},
'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'socialaccount.socialaccount': {
'Meta': {'object_name': 'SocialAccount'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
}
}
complete_apps = ['facebook']

View File

@ -0,0 +1,142 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models
class Migration(DataMigration):
depends_on = (('socialaccount', '0002_genericmodels'),)
def forwards(self, orm):
# Migrate FB apps
app_id_to_sapp = {}
for app in orm.FacebookApp.objects.all():
sapp = orm['socialaccount.SocialApp'].objects \
.create(site=app.site,
provider='facebook',
name=app.name,
key=app.application_id,
secret=app.application_secret)
app_id_to_sapp[app.id] = sapp
# Migrate FB accounts
acc_id_to_sacc = {}
for acc in orm.FacebookAccount.objects.all():
sacc = acc.socialaccount_ptr
sacc.uid = acc.social_id
sacc.extra_data = { 'link': acc.link,
'name': acc.name }
sacc.provider = 'facebook'
sacc.save()
acc_id_to_sacc[acc.id] = sacc
# Migrate tokens
for token in orm.FacebookAccessToken.objects.all():
sapp = app_id_to_sapp[token.app.id]
sacc = acc_id_to_sacc[token.account.id]
orm['socialaccount.SocialToken'].objects \
.create(app=sapp,
account=sacc,
token=token.access_token,
token_secret='')
def backwards(self, orm):
"Write your backwards methods here."
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'facebook.facebookaccesstoken': {
'Meta': {'unique_together': "(('app', 'account'),)", 'object_name': 'FacebookAccessToken'},
'access_token': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'account': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['facebook.FacebookAccount']"}),
'app': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['facebook.FacebookApp']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'facebook.facebookaccount': {
'Meta': {'object_name': 'FacebookAccount', '_ormbases': ['socialaccount.SocialAccount']},
'link': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'social_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
'socialaccount_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['socialaccount.SocialAccount']", 'unique': 'True', 'primary_key': 'True'})
},
'facebook.facebookapp': {
'Meta': {'object_name': 'FacebookApp'},
'api_key': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
'application_id': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
'application_secret': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"})
},
'sites.site': {
'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"},
'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'socialaccount.socialaccount': {
'Meta': {'object_name': 'SocialAccount'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'extra_data': ('allauth.socialaccount.fields.JSONField', [], {'default': "'{}'"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'provider': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'uid': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'socialaccount.socialapp': {
'Meta': {'object_name': 'SocialApp'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'key': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
'provider': ('django.db.models.fields.CharField', [], {'max_length': '30'}),
'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"})
},
'socialaccount.socialtoken': {
'Meta': {'unique_together': "(('app', 'account'),)", 'object_name': 'SocialToken'},
'account': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['socialaccount.SocialAccount']"}),
'app': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['socialaccount.SocialApp']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'token': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'token_secret': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'})
}
}
complete_apps = ['socialaccount', 'facebook']

View File

@ -0,0 +1,63 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Removing unique constraint on 'FacebookAccessToken', fields ['app', 'account']
db.delete_unique('facebook_facebookaccesstoken', ['app_id', 'account_id'])
# Deleting model 'FacebookApp'
db.delete_table('facebook_facebookapp')
# Deleting model 'FacebookAccessToken'
db.delete_table('facebook_facebookaccesstoken')
# Deleting model 'FacebookAccount'
db.delete_table('facebook_facebookaccount')
def backwards(self, orm):
# Adding model 'FacebookApp'
db.create_table('facebook_facebookapp', (
('application_id', self.gf('django.db.models.fields.CharField')(max_length=80)),
('name', self.gf('django.db.models.fields.CharField')(max_length=40)),
('site', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['sites.Site'])),
('api_key', self.gf('django.db.models.fields.CharField')(max_length=80)),
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('application_secret', self.gf('django.db.models.fields.CharField')(max_length=80)),
))
db.send_create_signal('facebook', ['FacebookApp'])
# Adding model 'FacebookAccessToken'
db.create_table('facebook_facebookaccesstoken', (
('access_token', self.gf('django.db.models.fields.CharField')(max_length=200)),
('account', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['facebook.FacebookAccount'])),
('app', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['facebook.FacebookApp'])),
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
))
db.send_create_signal('facebook', ['FacebookAccessToken'])
# Adding unique constraint on 'FacebookAccessToken', fields ['app', 'account']
db.create_unique('facebook_facebookaccesstoken', ['app_id', 'account_id'])
# Adding model 'FacebookAccount'
db.create_table('facebook_facebookaccount', (
('socialaccount_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['socialaccount.SocialAccount'], unique=True, primary_key=True)),
('social_id', self.gf('django.db.models.fields.CharField')(max_length=255, unique=True)),
('link', self.gf('django.db.models.fields.URLField')(max_length=200)),
('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
))
db.send_create_signal('facebook', ['FacebookAccount'])
models = {
}
complete_apps = ['facebook']

View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View File

@ -0,0 +1,92 @@
from django.core.urlresolvers import reverse
from django.core.exceptions import ImproperlyConfigured
from django.template.loader import render_to_string
from django.template import RequestContext
from allauth.utils import import_callable
from allauth.socialaccount import providers
from allauth.socialaccount.providers.base import ProviderAccount
from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider
from allauth.socialaccount.app_settings import QUERY_EMAIL
from allauth.socialaccount.models import SocialApp
from allauth.socialaccount.helpers import import_path
from locale import get_default_locale_callable
class FacebookAccount(ProviderAccount):
def get_profile_url(self):
return self.account.extra_data.get('link')
def get_avatar_url(self):
uid = self.account.uid
return 'http://graph.facebook.com/%s/picture?type=large' % uid
def __unicode__(self):
dflt = super(FacebookAccount, self).__unicode__()
return self.account.extra_data.get('name', dflt)
class FacebookProvider(OAuth2Provider):
id = 'facebook'
name = 'Facebook'
package = 'allauth.socialaccount.providers.facebook'
account_class = FacebookAccount
def __init__(self):
self._locale_callable_cache = None
super(FacebookProvider, self).__init__()
def get_method(self):
return self.get_settings().get('METHOD', 'oauth2')
def get_login_url(self, request, **kwargs):
method = kwargs.get('method', self.get_method())
if method == 'js_sdk':
next = "'%s'" % (kwargs.get('next') or '')
ret = "javascript:FB_login(%s)" % next
else:
assert method == 'oauth2'
ret = super(FacebookProvider, self).get_login_url(request,
**kwargs)
return ret
def _get_locale_callable(self):
settings = self.get_settings()
f = settings.get('LOCALE_FUNC')
if f:
f = import_callable(f)
else:
f = get_default_locale_callable()
return f
def get_locale_for_request(self, request):
if not self._locale_callable_cache:
self._locale_callable_cache = self._get_locale_callable()
return self._locale_callable_cache(request)
def get_default_scope(self):
scope = []
if QUERY_EMAIL:
scope.append('email')
return scope
def media_js(self, request):
perms = ','.join(self.get_scope())
locale = self.get_locale_for_request(request)
try:
app = self.get_app(request)
except SocialApp.DoesNotExist:
raise ImproperlyConfigured("No Facebook app configured: please"
" add a SocialApp using the Django"
" admin")
ctx = {'facebook_app': app,
'facebook_channel_url':
request.build_absolute_uri(reverse('facebook_channel')),
'facebook_perms': perms,
'facebook_jssdk_locale': locale}
return render_to_string('facebook/fbconnect.html',
ctx,
RequestContext(request))
providers.registry.register(FacebookProvider)

View File

@ -0,0 +1 @@
<script src="//connect.facebook.net/{{facebook_jssdk_locale}}/all.js"></script>

View File

@ -0,0 +1,46 @@
{% load url from future %}
<div id="fb-root"></div>
<script>
window.FB_login = function() {};
window.fbAsyncInit = function() {
FB.init({
appId : '{{facebook_app.key}}',
channelUrl : '{{facebook_channel_url}}',
status : true,
cookie : true,
oauth : true,
xfbml : true
});
window.FB_login = function(nextUrl) {
FB.login(function(response) {
if (response.authResponse) {
document.getElementById("_fb_access_token").value = response.authResponse.accessToken;
document.getElementById("_fb_expires_in").value = response.authResponse.expiresIn;
document.getElementById("_fb_next_url").value = nextUrl || '';
document.getElementById("_fb_login").submit();
} else {
var next;
if (response && response.status && response.status == "notConnected") {
next = '{% url 'socialaccount_login_cancelled' %}';
} else {
next = '{% url 'socialaccount_login_error' %}';
}
window.location.href = next;
}
}, {scope: '{{facebook_perms}}' });
}
};
(function(d){
var js, id = 'facebook-jssdk'; if (d.getElementById(id)) {return;}
js = d.createElement('script'); js.id = id; js.async = true;
js.src = "//connect.facebook.net/{{facebook_jssdk_locale}}/all.js";
d.getElementsByTagName('head')[0].appendChild(js);
}(document));
</script>
<form id="_fb_login" method="post" action="{% url 'facebook_login_by_token' %}">
{% csrf_token %}
<input type="hidden" name="next" value="" id="_fb_next_url"/>
<input type="hidden" name="access_token" id="_fb_access_token"/>
<input type="hidden" name="expires_in" id="_fb_expires_in"/>
</form>

View File

@ -0,0 +1,14 @@
from django.conf.urls.defaults import patterns, url
from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns
from provider import FacebookProvider
import views
urlpatterns = default_urlpatterns(FacebookProvider)
urlpatterns += patterns('',
url('^facebook/login/token/$', views.login_by_token,
name="facebook_login_by_token"),
url('^facebook/channel/$', views.channel, name='facebook_channel'),
)

View File

@ -0,0 +1,83 @@
from django.utils.cache import patch_response_headers
from django.contrib.auth.models import User
from django.shortcuts import render
from allauth.socialaccount.models import SocialAccount, SocialLogin, SocialToken
from allauth.socialaccount.helpers import complete_social_login
from allauth.socialaccount.helpers import render_authentication_error
from allauth.socialaccount import providers
from allauth.socialaccount.providers.oauth2.views import (OAuth2Adapter,
OAuth2LoginView,
OAuth2CallbackView)
from allauth.socialaccount import requests
from forms import FacebookConnectForm
from provider import FacebookProvider
from allauth.utils import valid_email_or_none
def fb_complete_login(app, token):
resp = requests.get('https://graph.facebook.com/me',
params={ 'access_token': token.token })
extra_data = resp.json
email = valid_email_or_none(extra_data.get('email'))
uid = extra_data['id']
user = User(email=email)
# some facebook accounts don't have this data
for k in ['username', 'first_name', 'last_name']:
v = extra_data.get(k)
if v:
setattr(user, k, v)
account = SocialAccount(uid=uid,
provider=FacebookProvider.id,
extra_data=extra_data,
user=user)
return SocialLogin(account)
class FacebookOAuth2Adapter(OAuth2Adapter):
provider_id = FacebookProvider.id
authorize_url = 'https://www.facebook.com/dialog/oauth'
access_token_url = 'https://graph.facebook.com/oauth/access_token'
def complete_login(self, request, app, access_token):
return fb_complete_login(app, access_token)
oauth2_login = OAuth2LoginView.adapter_view(FacebookOAuth2Adapter)
oauth2_callback = OAuth2CallbackView.adapter_view(FacebookOAuth2Adapter)
def login_by_token(request):
ret = None
if request.method == 'POST':
form = FacebookConnectForm(request.POST)
if form.is_valid():
try:
app = providers.registry.by_id(FacebookProvider.id) \
.get_app(request)
access_token = form.cleaned_data['access_token']
token = SocialToken(app=app,
token=access_token)
login = fb_complete_login(app, token)
login.token = token
login.state = SocialLogin.state_from_request(request)
ret = complete_social_login(request, login)
except:
# FIXME: Catch only what is needed
pass
if not ret:
ret = render_authentication_error(request)
return ret
def channel(request):
provider = providers.registry.by_id(FacebookProvider.id)
locale = provider.get_locale_for_request(request)
response = render(request, 'facebook/channel.html',
{'facebook_jssdk_locale': locale})
cache_expire = 60 * 60 * 24 * 365
patch_response_headers(response, cache_expire)
response['Pragma'] = 'Public'
return response

View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View File

@ -0,0 +1,24 @@
from allauth.socialaccount import providers
from allauth.socialaccount.providers.base import ProviderAccount
from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider
class GitHubAccount(ProviderAccount):
def get_profile_url(self):
return self.account.extra_data.get('html_url')
def get_avatar_url(self):
return self.account.extra_data.get('avatar_url')
def __unicode__(self):
dflt = super(GitHubAccount, self).__unicode__()
return self.account.extra_data.get('name', dflt)
class GitHubProvider(OAuth2Provider):
id = 'github'
name = 'GitHub'
package = 'allauth.socialaccount.providers.github'
account_class = GitHubAccount
providers.registry.register(GitHubProvider)

View File

@ -0,0 +1,5 @@
from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns
from provider import GitHubProvider
urlpatterns = default_urlpatterns(GitHubProvider)

View File

@ -0,0 +1,35 @@
from django.contrib.auth.models import User
from allauth.socialaccount.providers.oauth2.views import (OAuth2Adapter,
OAuth2LoginView,
OAuth2CallbackView)
from allauth.socialaccount import requests
from allauth.socialaccount.models import SocialAccount, SocialLogin
from provider import GitHubProvider
class GitHubOAuth2Adapter(OAuth2Adapter):
provider_id = GitHubProvider.id
access_token_url = 'https://github.com/login/oauth/access_token'
authorize_url = 'https://github.com/login/oauth/authorize'
profile_url = 'https://api.github.com/user'
def complete_login(self, request, app, token):
resp = requests.get(self.profile_url,
params={ 'access_token': token.token })
extra_data = resp.json
uid = str(extra_data['id'])
user = User(username=extra_data.get('login', ''),
email=extra_data.get('email', ''),
first_name=extra_data.get('name', ''))
account = SocialAccount(user=user,
uid=uid,
extra_data=extra_data,
provider=self.provider_id)
return SocialLogin(account)
oauth2_login = OAuth2LoginView.adapter_view(GitHubOAuth2Adapter)
oauth2_callback = OAuth2CallbackView.adapter_view(GitHubOAuth2Adapter)

View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View File

@ -0,0 +1,35 @@
from allauth.socialaccount import providers
from allauth.socialaccount.providers.base import ProviderAccount
from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider
from allauth.socialaccount.app_settings import QUERY_EMAIL
class Scope:
USERINFO_PROFILE = 'https://www.googleapis.com/auth/userinfo.profile'
USERINFO_EMAIL = 'https://www.googleapis.com/auth/userinfo.email'
class GoogleAccount(ProviderAccount):
def get_profile_url(self):
return self.account.extra_data.get('link')
def get_avatar_url(self):
return self.account.extra_data.get('picture')
def __unicode__(self):
dflt = super(GoogleAccount, self).__unicode__()
return self.account.extra_data.get('name', dflt)
class GoogleProvider(OAuth2Provider):
id = 'google'
name = 'Google'
package = 'allauth.socialaccount.providers.google'
account_class = GoogleAccount
def get_default_scope(self):
scope = [Scope.USERINFO_PROFILE]
if QUERY_EMAIL:
scope.append(Scope.USERINFO_EMAIL)
return scope
providers.registry.register(GoogleProvider)

View File

@ -0,0 +1,4 @@
from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns
from provider import GoogleProvider
urlpatterns = default_urlpatterns(GoogleProvider)

View File

@ -0,0 +1,46 @@
from django.contrib.auth.models import User
from allauth.socialaccount.providers.oauth2.views import (OAuth2Adapter,
OAuth2LoginView,
OAuth2CallbackView)
from allauth.socialaccount import requests
from allauth.socialaccount.models import SocialLogin, SocialAccount
from provider import GoogleProvider
class GoogleOAuth2Adapter(OAuth2Adapter):
provider_id = GoogleProvider.id
access_token_url = 'https://accounts.google.com/o/oauth2/token'
authorize_url = 'https://accounts.google.com/o/oauth2/auth'
profile_url = 'https://www.googleapis.com/oauth2/v1/userinfo'
def complete_login(self, request, app, token):
resp = requests.get(self.profile_url,
{ 'access_token': token.token,
'alt': 'json' })
extra_data = resp.json
# extra_data is something of the form:
#
# {u'family_name': u'Penners', u'name': u'Raymond Penners',
# u'picture': u'https://lh5.googleusercontent.com/-GOFYGBVOdBQ/AAAAAAAAAAI/AAAAAAAAAGM/WzRfPkv4xbo/photo.jpg',
# u'locale': u'nl', u'gender': u'male',
# u'email': u'raymond.penners@gmail.com',
# u'link': u'https://plus.google.com/108204268033311374519',
# u'given_name': u'Raymond', u'id': u'108204268033311374519',
# u'verified_email': True}
#
# TODO: We could use verified_email to bypass allauth email verification
uid = str(extra_data['id'])
user = User(email=extra_data.get('email', ''),
last_name=extra_data['family_name'],
first_name=extra_data['given_name'])
account = SocialAccount(extra_data=extra_data,
uid=uid,
provider=self.provider_id,
user=user)
return SocialLogin(account)
oauth2_login = OAuth2LoginView.adapter_view(GoogleOAuth2Adapter)
oauth2_callback = OAuth2CallbackView.adapter_view(GoogleOAuth2Adapter)

View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View File

@ -0,0 +1,15 @@
from allauth.socialaccount import providers
from allauth.socialaccount.providers.base import ProviderAccount
from allauth.socialaccount.providers.oauth.provider import OAuthProvider
class LinkedInAccount(ProviderAccount):
pass
class LinkedInProvider(OAuthProvider):
id = 'linkedin'
name = 'LinkedIn'
package = 'allauth.socialaccount.providers.linkedin'
account_class = LinkedInAccount
providers.registry.register(LinkedInProvider)

View File

@ -0,0 +1,4 @@
from allauth.socialaccount.providers.oauth.urls import default_urlpatterns
from provider import LinkedInProvider
urlpatterns = default_urlpatterns(LinkedInProvider)

View File

@ -0,0 +1,68 @@
from xml.etree import ElementTree
from xml.parsers.expat import ExpatError
from django.contrib.auth.models import User
from allauth.socialaccount.providers.oauth.client import OAuth
from allauth.socialaccount.providers.oauth.views import (OAuthAdapter,
OAuthLoginView,
OAuthCallbackView)
from allauth.socialaccount.models import SocialAccount, SocialLogin
from provider import LinkedInProvider
class LinkedInAPI(OAuth):
url = 'https://api.linkedin.com/v1/people/~'
fields = ['id', 'first-name', 'last-name']
def get_user_info(self):
url = self.url + ':(%s)' % ','.join(self.fields)
raw_xml = self.query(url)
try:
return self.to_dict(ElementTree.fromstring(raw_xml))
except (ExpatError, KeyError, IndexError):
return None
def to_dict(self, xml):
"""
Convert XML structure to dict recursively, repeated keys
entries are returned as in list containers.
"""
children = xml.getchildren()
if not children:
return xml.text
else:
out = {}
for node in xml.getchildren():
if node.tag in out:
if not isinstance(out[node.tag], list):
out[node.tag] = [out[node.tag]]
out[node.tag].append(self.to_dict(node))
else:
out[node.tag] = self.to_dict(node)
return out
class LinkedInOAuthAdapter(OAuthAdapter):
provider_id = LinkedInProvider.id
request_token_url = 'https://api.linkedin.com/uas/oauth/requestToken'
access_token_url = 'https://api.linkedin.com/uas/oauth/accessToken'
authorize_url = 'https://www.linkedin.com/uas/oauth/authenticate'
def complete_login(self, request, app, token):
client = LinkedInAPI(request, app.key, app.secret,
self.request_token_url)
extra_data = client.get_user_info()
uid = extra_data['id']
user = User(first_name=extra_data.get('first-name', ''),
last_name=extra_data.get('last-name', ''))
account = SocialAccount(user=user,
provider=self.provider_id,
extra_data=extra_data,
uid=uid)
return SocialLogin(account)
oauth_login = OAuthLoginView.adapter_view(LinkedInOAuthAdapter)
oauth_callback = OAuthCallbackView.adapter_view(LinkedInOAuthAdapter)

View File

@ -0,0 +1,185 @@
"""
Parts derived from socialregistration and authorized by: alen, pinda
Inspired by:
http://github.com/leah/python-oauth/blob/master/oauth/example/client.py
http://github.com/facebook/tornado/blob/master/tornado/auth.py
"""
import urllib
import urllib2
from django.http import HttpResponseRedirect
from django.utils.translation import gettext as _
# parse_qsl was moved from the cgi namespace to urlparse in Python2.6.
# this allows backwards compatibility
try:
from urlparse import parse_qsl
except ImportError:
from cgi import parse_qsl
import oauth2 as oauth
def get_token_prefix(url):
"""
Returns a prefix for the token to store in the session so we can hold
more than one single oauth provider's access key in the session.
Example:
The request token url ``http://twitter.com/oauth/request_token``
returns ``twitter.com``
"""
return urllib2.urlparse.urlparse(url).netloc
class OAuthError(Exception):
pass
class OAuthClient(object):
def __init__(self, request, consumer_key, consumer_secret, request_token_url,
access_token_url, authorization_url, callback_url, parameters=None):
self.request = request
self.request_token_url = request_token_url
self.access_token_url = access_token_url
self.authorization_url = authorization_url
self.consumer_key = consumer_key
self.consumer_secret = consumer_secret
self.consumer = oauth.Consumer(consumer_key, consumer_secret)
self.client = oauth.Client(self.consumer)
self.signature_method = oauth.SignatureMethod_HMAC_SHA1()
self.parameters = parameters
self.callback_url = callback_url
self.errors = []
self.request_token = None
self.access_token = None
def _get_request_token(self):
"""
Obtain a temporary request token to authorize an access token and to
sign the request to obtain the access token
"""
if self.request_token is None:
rt_url = self.request_token_url + '?' + urllib.urlencode({'oauth_callback': self.request.build_absolute_uri(self.callback_url)})
response, content = self.client.request(rt_url, "GET")
if response['status'] != '200':
raise OAuthError(
_('Invalid response while obtaining request token from "%s".') % get_token_prefix(self.request_token_url))
self.request_token = dict(parse_qsl(content))
self.request.session['oauth_%s_request_token' % get_token_prefix(self.request_token_url)] = self.request_token
return self.request_token
def get_access_token(self):
"""
Obtain the access token to access private resources at the API endpoint.
"""
if self.access_token is None:
request_token = self._get_rt_from_session()
token = oauth.Token(request_token['oauth_token'], request_token['oauth_token_secret'])
self.client = oauth.Client(self.consumer, token)
at_url = self.access_token_url
# Passing along oauth_verifier is required according to:
# http://groups.google.com/group/twitter-development-talk/browse_frm/thread/472500cfe9e7cdb9#
# Though, the custom oauth_callback seems to work without it?
if self.request.REQUEST.has_key('oauth_verifier'):
at_url = at_url + '?' + urllib.urlencode({'oauth_verifier': self.request.REQUEST['oauth_verifier']})
response, content = self.client.request(at_url, "GET")
if response['status'] != '200':
raise OAuthError(
_('Invalid response while obtaining access token from "%s".') % get_token_prefix(self.request_token_url))
self.access_token = dict(parse_qsl(content))
self.request.session['oauth_%s_access_token' % get_token_prefix(self.request_token_url)] = self.access_token
return self.access_token
def _get_rt_from_session(self):
"""
Returns the request token cached in the session by ``_get_request_token``
"""
try:
return self.request.session['oauth_%s_request_token' % get_token_prefix(self.request_token_url)]
except KeyError:
raise OAuthError(_('No request token saved for "%s".') % get_token_prefix(self.request_token_url))
def _get_authorization_url(self):
request_token = self._get_request_token()
return '%s?oauth_token=%s&oauth_callback=%s' % (self.authorization_url,
request_token['oauth_token'], self.request.build_absolute_uri(self.callback_url))
def is_valid(self):
try:
self._get_rt_from_session()
self.get_access_token()
except OAuthError, e:
self.errors.append(e.args[0])
return False
return True
def get_redirect(self):
"""
Returns a ``HttpResponseRedirect`` object to redirect the user to the
URL the OAuth provider handles authorization.
"""
return HttpResponseRedirect(self._get_authorization_url())
class OAuth(object):
"""
Base class to perform oauth signed requests from access keys saved in a user's
session.
See the ``OAuthTwitter`` class below for an example.
"""
def __init__(self, request, consumer_key, secret_key, request_token_url):
self.request = request
self.consumer_key = consumer_key
self.secret_key = secret_key
self.consumer = oauth.Consumer(consumer_key, secret_key)
self.request_token_url = request_token_url
def _get_at_from_session(self):
"""
Get the saved access token for private resources from the session.
"""
try:
return self.request.session['oauth_%s_access_token' % get_token_prefix(self.request_token_url)]
except KeyError:
raise OAuthError(
_('No access token saved for "%s".') % get_token_prefix(self.request_token_url))
def query(self, url, method="GET", params=dict(), headers=dict()):
"""
Request a API endpoint at ``url`` with ``params`` being either the
POST or GET data.
"""
access_token = self._get_at_from_session()
token = oauth.Token(access_token['oauth_token'], access_token['oauth_token_secret'])
client = oauth.Client(self.consumer, token)
body = urllib.urlencode(params)
response, content = client.request(url, method=method, headers=headers,
body=body)
if response['status'] != '200':
raise OAuthError(
_('No access to private resources at "%s".') % get_token_prefix(self.request_token_url))
return content

View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View File

@ -0,0 +1,13 @@
from django.core.urlresolvers import reverse
from django.utils.http import urlencode
from allauth.socialaccount.providers.base import Provider
class OAuthProvider(Provider):
def get_login_url(self, request, **kwargs):
url = reverse(self.id + "_login")
if kwargs:
url = url + '?' + urlencode(kwargs)
return url

View File

@ -0,0 +1,12 @@
from django.conf.urls.defaults import patterns, url, include
def default_urlpatterns(provider):
urlpatterns = patterns(provider.package + '.views',
url('^login/$', 'oauth_login',
name=provider.id + "_login"),
url('^login/callback/$', 'oauth_callback',
name=provider.id + "_callback"))
return patterns('', url('^' + provider.id + '/', include(urlpatterns)))

View File

@ -0,0 +1,84 @@
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.utils.http import urlencode
from allauth.socialaccount.helpers import render_authentication_error
from allauth.socialaccount.providers.oauth.client import (OAuthClient,
OAuthError)
from allauth.socialaccount.helpers import complete_social_login
from allauth.socialaccount import providers
from allauth.socialaccount.models import SocialToken, SocialLogin
class OAuthAdapter(object):
def complete_login(self, request, app):
"""
Returns a SocialLogin instance
"""
raise NotImplementedError
def get_provider(self):
return providers.registry.by_id(self.provider_id)
class OAuthView(object):
@classmethod
def adapter_view(cls, adapter):
def view(request, *args, **kwargs):
self = cls()
self.request = request
self.adapter = adapter()
return self.dispatch(request, *args, **kwargs)
return view
def _get_client(self, request, callback_url):
app = self.adapter.get_provider().get_app(request)
client = OAuthClient(request, app.key, app.secret,
self.adapter.request_token_url,
self.adapter.access_token_url,
self.adapter.authorize_url,
callback_url)
return client
class OAuthLoginView(OAuthView):
def dispatch(self, request):
callback_url = reverse(self.adapter.provider_id + "_callback")
# TODO: Can't this be moved as query param into callback?
# Tried but failed somehow, needs further study...
request.session['oauth_login_state'] \
= SocialLogin.marshall_state(request)
client = self._get_client(request, callback_url)
try:
return client.get_redirect()
except OAuthError:
return render_authentication_error(request)
class OAuthCallbackView(OAuthView):
def dispatch(self, request):
"""
View to handle final steps of OAuth based authentication where the user
gets redirected back to from the service provider
"""
login_done_url = reverse(self.adapter.provider_id + "_callback")
client = self._get_client(request, login_done_url)
if not client.is_valid():
if request.GET.has_key('denied'):
return HttpResponseRedirect(reverse('socialaccount_login_cancelled'))
extra_context = dict(oauth_client=client)
return render_authentication_error(request, extra_context)
app = self.adapter.get_provider().get_app(request)
try:
access_token = client.get_access_token()
token = SocialToken(app=app,
token=access_token['oauth_token'],
token_secret=access_token['oauth_token_secret'])
login = self.adapter.complete_login(request, app, token)
token.account = login.account
login.token = token
login.state = SocialLogin.unmarshall_state \
(request.session.pop('oauth_login_state', None))
return complete_social_login(request, login)
except OAuthError:
return render_authentication_error(request)

View File

@ -0,0 +1,58 @@
import urllib
import urlparse
from allauth.socialaccount import requests
class OAuth2Error(Exception):
pass
class OAuth2Client(object):
def __init__(self, request, consumer_key, consumer_secret,
authorization_url,
access_token_url,
callback_url,
scope):
self.request = request
self.authorization_url = authorization_url
self.access_token_url = access_token_url
self.callback_url = callback_url
self.consumer_key = consumer_key
self.consumer_secret = consumer_secret
self.scope = ' '.join(scope)
self.state = None
def get_redirect_url(self):
params = {
'client_id': self.consumer_key,
'redirect_uri': self.callback_url,
'scope': self.scope,
'response_type': 'code'
}
if self.state:
params['state'] = self.state
return '%s?%s' % (self.authorization_url, urllib.urlencode(params))
def get_access_token(self, code):
params = {'client_id': self.consumer_key,
'redirect_uri': self.callback_url,
'grant_type': 'authorization_code',
'client_secret': self.consumer_secret,
'scope': self.scope,
'code': code}
url = self.access_token_url
# TODO: Proper exception handling
resp = requests.post(url, params)
access_token = None
if resp.status_code == 200:
if resp.headers['content-type'] == 'application/json':
data = resp.json
else:
data = dict(urlparse.parse_qsl(resp.content))
access_token = data.get('access_token')
if not access_token:
raise OAuth2Error('Error retrieving access token: %s'
% resp.content)
return access_token

View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View File

@ -0,0 +1,23 @@
from django.core.urlresolvers import reverse
from django.utils.http import urlencode
from allauth.socialaccount.providers.base import Provider
class OAuth2Provider(Provider):
def get_login_url(self, request, **kwargs):
url = reverse(self.id + "_login")
if kwargs:
url = url + '?' + urlencode(kwargs)
return url
def get_scope(self):
settings = self.get_settings()
scope = settings.get('SCOPE')
if scope is None:
scope = self.get_default_scope()
return scope
def get_default_scope(self):
return []

View File

@ -0,0 +1,11 @@
from django.conf.urls.defaults import patterns, url, include
def default_urlpatterns(provider):
urlpatterns = patterns(provider.package + '.views',
url('^login/$', 'oauth2_login',
name=provider.id + "_login"),
url('^login/callback/$', 'oauth2_callback',
name=provider.id + "_callback"))
return patterns('', url('^' + provider.id + '/', include(urlpatterns)))

View File

@ -0,0 +1,76 @@
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from allauth.socialaccount.helpers import render_authentication_error
from allauth.socialaccount import providers
from allauth.socialaccount.providers.oauth2.client import (OAuth2Client,
OAuth2Error)
from allauth.socialaccount.helpers import complete_social_login
from allauth.socialaccount.models import SocialToken, SocialLogin
class OAuth2Adapter(object):
def get_provider(self):
return providers.registry.by_id(self.provider_id)
def complete_login(self, request, app, access_token):
"""
Returns a SocialLogin instance
"""
raise NotImplementedError
class OAuth2View(object):
@classmethod
def adapter_view(cls, adapter):
def view(request, *args, **kwargs):
self = cls()
self.request = request
self.adapter = adapter()
return self.dispatch(request, *args, **kwargs)
return view
def get_client(self, request, app):
callback_url = reverse(self.adapter.provider_id + "_callback")
callback_url = request.build_absolute_uri(callback_url)
client = OAuth2Client(self.request, app.key, app.secret,
self.adapter.authorize_url,
self.adapter.access_token_url,
callback_url,
self.adapter.get_provider().get_scope())
return client
class OAuth2LoginView(OAuth2View):
def dispatch(self, request):
app = self.adapter.get_provider().get_app(self.request)
client = self.get_client(request, app)
client.state = SocialLogin.marshall_state(request)
try:
return HttpResponseRedirect(client.get_redirect_url())
except OAuth2Error:
return render_authentication_error(request)
class OAuth2CallbackView(OAuth2View):
def dispatch(self, request):
if 'error' in request.GET or not 'code' in request.GET:
# TODO: Distinguish cancel from error
return render_authentication_error(request)
app = self.adapter.get_provider().get_app(self.request)
client = self.get_client(request, app)
try:
access_token = client.get_access_token(request.GET['code'])
token = SocialToken(app=app,
token=access_token)
login = self.adapter.complete_login(request,
app,
token)
token.account = login.account
login.token = token
login.state = SocialLogin.unmarshall_state(request.REQUEST
.get('state'))
return complete_social_login(request, login)
except OAuth2Error:
return render_authentication_error(request)

View File

@ -0,0 +1,13 @@
from django.contrib import admin
from models import OpenIDStore, OpenIDNonce
class OpenIDStoreAdmin(admin.ModelAdmin):
pass
class OpenIDNonceAdmin(admin.ModelAdmin):
pass
admin.site.register(OpenIDStore, OpenIDStoreAdmin)
admin.site.register(OpenIDNonce, OpenIDNonceAdmin)

View File

@ -0,0 +1,7 @@
from django import forms
class LoginForm(forms.Form):
openid = forms.URLField(label=('OpenID'),
help_text='Get an <a href="http://openid.net/get-an-openid/">OpenID</a>')

View File

@ -0,0 +1,123 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
depends_on = (('socialaccount', '0001_initial'),)
def forwards(self, orm):
# Adding model 'OpenIDAccount'
db.create_table('openid_openidaccount', (
('socialaccount_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['socialaccount.SocialAccount'], unique=True, primary_key=True)),
('identity', self.gf('django.db.models.fields.URLField')(unique=True, max_length=255)),
))
db.send_create_signal('openid', ['OpenIDAccount'])
# Adding model 'OpenIDStore'
db.create_table('openid_openidstore', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('server_url', self.gf('django.db.models.fields.CharField')(max_length=255)),
('handle', self.gf('django.db.models.fields.CharField')(max_length=255)),
('secret', self.gf('django.db.models.fields.TextField')()),
('issued', self.gf('django.db.models.fields.IntegerField')()),
('lifetime', self.gf('django.db.models.fields.IntegerField')()),
('assoc_type', self.gf('django.db.models.fields.TextField')()),
))
db.send_create_signal('openid', ['OpenIDStore'])
# Adding model 'OpenIDNonce'
db.create_table('openid_openidnonce', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('server_url', self.gf('django.db.models.fields.CharField')(max_length=255)),
('timestamp', self.gf('django.db.models.fields.IntegerField')()),
('salt', self.gf('django.db.models.fields.CharField')(max_length=255)),
('date_created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
))
db.send_create_signal('openid', ['OpenIDNonce'])
def backwards(self, orm):
# Deleting model 'OpenIDAccount'
db.delete_table('openid_openidaccount')
# Deleting model 'OpenIDStore'
db.delete_table('openid_openidstore')
# Deleting model 'OpenIDNonce'
db.delete_table('openid_openidnonce')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'openid.openidaccount': {
'Meta': {'object_name': 'OpenIDAccount', '_ormbases': ['socialaccount.SocialAccount']},
'identity': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '255'}),
'socialaccount_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['socialaccount.SocialAccount']", 'unique': 'True', 'primary_key': 'True'})
},
'openid.openidnonce': {
'Meta': {'object_name': 'OpenIDNonce'},
'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'salt': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'server_url': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'timestamp': ('django.db.models.fields.IntegerField', [], {})
},
'openid.openidstore': {
'Meta': {'object_name': 'OpenIDStore'},
'assoc_type': ('django.db.models.fields.TextField', [], {}),
'handle': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'issued': ('django.db.models.fields.IntegerField', [], {}),
'lifetime': ('django.db.models.fields.IntegerField', [], {}),
'secret': ('django.db.models.fields.TextField', [], {}),
'server_url': ('django.db.models.fields.CharField', [], {'max_length': '255'})
},
'socialaccount.socialaccount': {
'Meta': {'object_name': 'SocialAccount'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
}
}
complete_apps = ['openid']

View File

@ -0,0 +1,118 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models
class Migration(DataMigration):
depends_on = (('socialaccount', '0002_genericmodels'),)
def forwards(self, orm):
for acc in orm.OpenIDAccount.objects.all():
sacc = acc.socialaccount_ptr
sacc.uid = acc.identity
sacc.provider = 'openid'
sacc.save()
def backwards(self, orm):
"Write your backwards methods here."
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'openid.openidaccount': {
'Meta': {'object_name': 'OpenIDAccount', '_ormbases': ['socialaccount.SocialAccount']},
'identity': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '255'}),
'socialaccount_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['socialaccount.SocialAccount']", 'unique': 'True', 'primary_key': 'True'})
},
'openid.openidnonce': {
'Meta': {'object_name': 'OpenIDNonce'},
'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'salt': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'server_url': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'timestamp': ('django.db.models.fields.IntegerField', [], {})
},
'openid.openidstore': {
'Meta': {'object_name': 'OpenIDStore'},
'assoc_type': ('django.db.models.fields.TextField', [], {}),
'handle': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'issued': ('django.db.models.fields.IntegerField', [], {}),
'lifetime': ('django.db.models.fields.IntegerField', [], {}),
'secret': ('django.db.models.fields.TextField', [], {}),
'server_url': ('django.db.models.fields.CharField', [], {'max_length': '255'})
},
'sites.site': {
'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"},
'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'socialaccount.socialaccount': {
'Meta': {'object_name': 'SocialAccount'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'extra_data': ('allauth.socialaccount.fields.JSONField', [], {'default': "'{}'"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'provider': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'uid': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'socialaccount.socialapp': {
'Meta': {'object_name': 'SocialApp'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'key': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
'provider': ('django.db.models.fields.CharField', [], {'max_length': '30'}),
'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"})
},
'socialaccount.socialtoken': {
'Meta': {'unique_together': "(('app', 'account'),)", 'object_name': 'SocialToken'},
'account': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['socialaccount.SocialAccount']"}),
'app': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['socialaccount.SocialApp']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'token': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'token_secret': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'})
}
}
complete_apps = ['socialaccount', 'openid']

View File

@ -0,0 +1,46 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Deleting model 'OpenIDAccount'
db.delete_table('openid_openidaccount')
def backwards(self, orm):
# Adding model 'OpenIDAccount'
db.create_table('openid_openidaccount', (
('socialaccount_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['socialaccount.SocialAccount'], unique=True, primary_key=True)),
('identity', self.gf('django.db.models.fields.URLField')(max_length=255, unique=True)),
))
db.send_create_signal('openid', ['OpenIDAccount'])
models = {
'openid.openidnonce': {
'Meta': {'object_name': 'OpenIDNonce'},
'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'salt': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'server_url': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'timestamp': ('django.db.models.fields.IntegerField', [], {})
},
'openid.openidstore': {
'Meta': {'object_name': 'OpenIDStore'},
'assoc_type': ('django.db.models.fields.TextField', [], {}),
'handle': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'issued': ('django.db.models.fields.IntegerField', [], {}),
'lifetime': ('django.db.models.fields.IntegerField', [], {}),
'secret': ('django.db.models.fields.TextField', [], {}),
'server_url': ('django.db.models.fields.CharField', [], {'max_length': '255'})
}
}
complete_apps = ['openid']

View File

@ -0,0 +1,22 @@
from django.db import models
class OpenIDStore(models.Model):
server_url = models.CharField(max_length=255)
handle = models.CharField(max_length=255)
secret = models.TextField()
issued = models.IntegerField()
lifetime = models.IntegerField()
assoc_type = models.TextField()
def __unicode__(self):
return self.server_url
class OpenIDNonce(models.Model):
server_url = models.CharField(max_length=255)
timestamp = models.IntegerField()
salt = models.CharField(max_length=255)
date_created = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return self.server_url

View File

@ -0,0 +1,58 @@
from urlparse import urlparse
from django.core.urlresolvers import reverse
from django.utils.http import urlencode
from allauth.socialaccount import providers
from allauth.socialaccount.providers.base import Provider, ProviderAccount
class OpenIDAccount(ProviderAccount):
def get_brand(self):
ret = super(OpenIDAccount, self).get_brand()
domain = urlparse(self.account.uid).netloc
# FIXME: Instead of hardcoding, derive this from the domains
# listed in the openid endpoints setting.
provider_map = {'yahoo': dict(id='yahoo',
name='Yahoo'),
'hyves': dict(id='hyves',
name='Hyves'),
'google': dict(id='google',
name='Google')}
for d, p in provider_map.iteritems():
if domain.lower().find(d) >= 0:
ret = p
break
return ret
def __unicode__(self):
return self.account.uid
class OpenIDProvider(Provider):
id = 'openid'
name = 'OpenID'
package = 'allauth.socialaccount.providers.openid'
account_class = OpenIDAccount
def get_login_url(self, request, next=None, openid=None):
url = reverse('openid_login')
query = {}
if openid:
query['openid'] = openid
if next:
query['next'] = next
if query:
url += '?' + urlencode(query)
return url
def get_brands(self):
# These defaults are a bit too arbitrary...
default_servers = [dict(id='yahoo',
name='Yahoo',
openid_url='http://me.yahoo.com'),
dict(id='hyves',
name='Hyves',
openid_url='http://hyves.nl')]
return self.get_settings().get('SERVERS', default_servers)
providers.registry.register(OpenIDProvider)

View File

@ -0,0 +1,16 @@
from django.test import TestCase
from django.core.urlresolvers import reverse
import views
class OpenIDTests(TestCase):
def test_discovery_failure(self):
"""
This used to generate a server 500:
DiscoveryFailure: No usable OpenID services found for http://www.google.com/
"""
resp = self.client.post(reverse(views.login),
dict(openid='http://www.google.com'))
self.assertTrue(resp.context['form'].errors.has_key('openid'))

View File

@ -0,0 +1,8 @@
from django.conf.urls.defaults import patterns, url
import views
urlpatterns = patterns('',
url('^openid/login/$', views.login, name="openid_login"),
url('^openid/callback/$', views.callback),
)

View File

@ -0,0 +1,76 @@
import base64
from django.http import HttpResponseRedirect
from openid.store.interface import OpenIDStore as OIDStore
from openid.association import Association as OIDAssociation
from models import OpenIDStore, OpenIDNonce
class DBOpenIDStore(OIDStore):
max_nonce_age = 6 * 60 * 60
def storeAssociation(self, server_url, assoc=None):
stored_assoc = OpenIDStore.objects.create(
server_url=server_url,
handle=assoc.handle,
secret=base64.encodestring(assoc.secret),
issued=assoc.issued,
lifetime=assoc.lifetime,
assoc_type=assoc.assoc_type
)
def getAssociation(self, server_url, handle=None):
stored_assocs = OpenIDStore.objects.filter(
server_url=server_url
)
if handle:
stored_assocs = stored_assocs.filter(handle=handle)
stored_assocs.order_by('-issued')
if stored_assocs.count() == 0:
return None
return_val = None
for stored_assoc in stored_assocs:
assoc = OIDAssociation(
stored_assoc.handle, base64.decodestring(stored_assoc.secret),
stored_assoc.issued, stored_assoc.lifetime, stored_assoc.assoc_type
)
if assoc.getExpiresIn() == 0:
stored_assoc.delete()
else:
if return_val is None:
return_val = assoc
return return_val
def removeAssociation(self, server_url, handle):
stored_assocs = OpenIDStore.objects.filter(
server_url=server_url
)
if handle:
stored_assocs = stored_assocs.filter(handle=handle)
stored_assocs.delete()
def useNonce(self, server_url, timestamp, salt):
try:
nonce = OpenIDNonce.objects.get(
server_url=server_url,
timestamp=timestamp,
salt=salt
)
except OpenIDNonce.DoesNotExist:
nonce = OpenIDNonce.objects.create(
server_url=server_url,
timestamp=timestamp,
salt=salt
)
return True
return False

View File

@ -0,0 +1,109 @@
from django.contrib.auth.models import User
from django.utils.http import urlencode
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.views.decorators.csrf import csrf_exempt
from openid.consumer.discover import DiscoveryFailure
from openid.consumer import consumer
from openid.extensions.sreg import SRegRequest, SRegResponse
from openid.extensions.ax import FetchRequest, FetchResponse, AttrInfo
from allauth.socialaccount.app_settings import QUERY_EMAIL
from allauth.socialaccount.models import SocialAccount, SocialLogin
from allauth.socialaccount.helpers import render_authentication_error
from allauth.socialaccount.helpers import complete_social_login
from allauth.utils import valid_email_or_none
from utils import DBOpenIDStore
from forms import LoginForm
from provider import OpenIDProvider
class AXAttribute:
CONTACT_EMAIL = 'http://axschema.org/contact/email'
class SRegField:
EMAIL = 'email'
def _openid_consumer(request):
store = DBOpenIDStore()
client = consumer.Consumer(request.session, store)
return client
def login(request):
if request.GET.has_key('openid') or request.method == 'POST':
form = LoginForm(request.REQUEST)
if form.is_valid():
client = _openid_consumer(request)
try:
auth_request = client.begin(form.cleaned_data['openid'])
if QUERY_EMAIL:
sreg = SRegRequest()
sreg.requestField(field_name=SRegField.EMAIL, required=True)
auth_request.addExtension(sreg)
ax = FetchRequest()
ax.add(AttrInfo(AXAttribute.CONTACT_EMAIL,
required=True))
auth_request.addExtension(ax)
callback_url = reverse(callback)
state = SocialLogin.marshall_state(request)
callback_url = callback_url + '?' + urlencode(dict(state=state))
redirect_url = auth_request.redirectURL(
request.build_absolute_uri('/'),
request.build_absolute_uri(callback_url))
return HttpResponseRedirect(redirect_url)
except DiscoveryFailure, e:
if request.method == 'POST':
form._errors["openid"] = form.error_class([e])
else:
return render_authentication_error(request)
else:
form = LoginForm()
d = dict(form=form)
return render_to_response('openid/login.html',
d, context_instance=RequestContext(request))
def _get_email_from_response(response):
email = None
sreg = SRegResponse.fromSuccessResponse(response)
if sreg:
email = valid_email_or_none(sreg.get(SRegField.EMAIL))
if not email:
ax = FetchResponse.fromSuccessResponse(response)
if ax:
try:
values = ax.get(AXAttribute.CONTACT_EMAIL)
if values:
email = valid_email_or_none(values[0])
except KeyError:
pass
return email
@csrf_exempt
def callback(request):
client = _openid_consumer(request)
response = client.complete(
dict(request.REQUEST.items()),
request.build_absolute_uri(request.path))
if response.status == consumer.SUCCESS:
user = User(email=_get_email_from_response(response))
account = SocialAccount(uid=response.identity_url,
provider=OpenIDProvider.id,
user=user,
extra_data={})
login = SocialLogin(account)
login.state = SocialLogin.unmarshall_state(request.REQUEST.get('state'))
ret = complete_social_login(request, login)
elif response.status == consumer.CANCEL:
ret = HttpResponseRedirect(reverse('socialaccount_login_cancelled'))
else:
ret = render_authentication_error(request)
return ret

View File

@ -0,0 +1,115 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
depends_on = (('socialaccount', '0001_initial'),)
def forwards(self, orm):
# Adding model 'TwitterApp'
db.create_table('twitter_twitterapp', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('site', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['sites.Site'])),
('name', self.gf('django.db.models.fields.CharField')(max_length=40)),
('consumer_key', self.gf('django.db.models.fields.CharField')(max_length=80)),
('consumer_secret', self.gf('django.db.models.fields.CharField')(max_length=80)),
('request_token_url', self.gf('django.db.models.fields.URLField')(max_length=200)),
('access_token_url', self.gf('django.db.models.fields.URLField')(max_length=200)),
('authorize_url', self.gf('django.db.models.fields.URLField')(max_length=200)),
))
db.send_create_signal('twitter', ['TwitterApp'])
# Adding model 'TwitterAccount'
db.create_table('twitter_twitteraccount', (
('socialaccount_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['socialaccount.SocialAccount'], unique=True, primary_key=True)),
('social_id', self.gf('django.db.models.fields.PositiveIntegerField')(unique=True)),
('username', self.gf('django.db.models.fields.CharField')(max_length=15)),
('profile_image_url', self.gf('django.db.models.fields.URLField')(max_length=200)),
))
db.send_create_signal('twitter', ['TwitterAccount'])
def backwards(self, orm):
# Deleting model 'TwitterApp'
db.delete_table('twitter_twitterapp')
# Deleting model 'TwitterAccount'
db.delete_table('twitter_twitteraccount')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'sites.site': {
'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"},
'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'socialaccount.socialaccount': {
'Meta': {'object_name': 'SocialAccount'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'twitter.twitteraccount': {
'Meta': {'object_name': 'TwitterAccount', '_ormbases': ['socialaccount.SocialAccount']},
'profile_image_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
'social_id': ('django.db.models.fields.PositiveIntegerField', [], {'unique': 'True'}),
'socialaccount_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['socialaccount.SocialAccount']", 'unique': 'True', 'primary_key': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'max_length': '15'})
},
'twitter.twitterapp': {
'Meta': {'object_name': 'TwitterApp'},
'access_token_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
'authorize_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
'consumer_key': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
'consumer_secret': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
'request_token_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"})
}
}
complete_apps = ['twitter']

View File

@ -0,0 +1,91 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Changing field 'TwitterAccount.social_id'
db.alter_column('twitter_twitteraccount', 'social_id', self.gf('django.db.models.fields.BigIntegerField')(unique=True))
def backwards(self, orm):
# Changing field 'TwitterAccount.social_id'
db.alter_column('twitter_twitteraccount', 'social_id', self.gf('django.db.models.fields.PositiveIntegerField')(unique=True))
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'sites.site': {
'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"},
'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'socialaccount.socialaccount': {
'Meta': {'object_name': 'SocialAccount'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'twitter.twitteraccount': {
'Meta': {'object_name': 'TwitterAccount', '_ormbases': ['socialaccount.SocialAccount']},
'profile_image_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
'social_id': ('django.db.models.fields.BigIntegerField', [], {'unique': 'True'}),
'socialaccount_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['socialaccount.SocialAccount']", 'unique': 'True', 'primary_key': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'max_length': '15'})
},
'twitter.twitterapp': {
'Meta': {'object_name': 'TwitterApp'},
'access_token_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
'authorize_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
'consumer_key': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
'consumer_secret': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
'request_token_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"})
}
}
complete_apps = ['twitter']

View File

@ -0,0 +1,128 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models
class Migration(DataMigration):
depends_on = (('socialaccount', '0002_genericmodels'),)
def forwards(self, orm):
# Migrate apps
app_id_to_sapp = {}
for app in orm.TwitterApp.objects.all():
sapp = orm['socialaccount.SocialApp'].objects \
.create(site=app.site,
provider='twitter',
name=app.name,
key=app.consumer_key,
secret=app.consumer_secret)
app_id_to_sapp[app.id] = sapp
# Migrate accounts
acc_id_to_sacc = {}
for acc in orm.TwitterAccount.objects.all():
sacc = acc.socialaccount_ptr
sacc.uid = str(acc.social_id)
sacc.extra_data = { 'screen_name': acc.username,
'profile_image_url': acc.profile_image_url }
sacc.provider = 'twitter'
sacc.save()
acc_id_to_sacc[acc.id] = sacc
def backwards(self, orm):
"Write your backwards methods here."
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'sites.site': {
'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"},
'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'socialaccount.socialaccount': {
'Meta': {'object_name': 'SocialAccount'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'extra_data': ('allauth.socialaccount.fields.JSONField', [], {'default': "'{}'"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'provider': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'uid': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'socialaccount.socialapp': {
'Meta': {'object_name': 'SocialApp'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'key': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
'provider': ('django.db.models.fields.CharField', [], {'max_length': '30'}),
'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"})
},
'socialaccount.socialtoken': {
'Meta': {'unique_together': "(('app', 'account'),)", 'object_name': 'SocialToken'},
'account': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['socialaccount.SocialAccount']"}),
'app': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['socialaccount.SocialApp']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'token': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'token_secret': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'})
},
'twitter.twitteraccount': {
'Meta': {'object_name': 'TwitterAccount', '_ormbases': ['socialaccount.SocialAccount']},
'profile_image_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
'social_id': ('django.db.models.fields.BigIntegerField', [], {'unique': 'True'}),
'socialaccount_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['socialaccount.SocialAccount']", 'unique': 'True', 'primary_key': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'max_length': '15'})
},
'twitter.twitterapp': {
'Meta': {'object_name': 'TwitterApp'},
'access_token_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
'authorize_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
'consumer_key': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
'consumer_secret': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
'request_token_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"})
}
}
complete_apps = ['socialaccount', 'twitter']

View File

@ -0,0 +1,47 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Deleting model 'TwitterAccount'
db.delete_table('twitter_twitteraccount')
# Deleting model 'TwitterApp'
db.delete_table('twitter_twitterapp')
def backwards(self, orm):
# Adding model 'TwitterAccount'
db.create_table('twitter_twitteraccount', (
('username', self.gf('django.db.models.fields.CharField')(max_length=15)),
('social_id', self.gf('django.db.models.fields.BigIntegerField')(unique=True)),
('socialaccount_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['socialaccount.SocialAccount'], unique=True, primary_key=True)),
('profile_image_url', self.gf('django.db.models.fields.URLField')(max_length=200)),
))
db.send_create_signal('twitter', ['TwitterAccount'])
# Adding model 'TwitterApp'
db.create_table('twitter_twitterapp', (
('consumer_secret', self.gf('django.db.models.fields.CharField')(max_length=80)),
('request_token_url', self.gf('django.db.models.fields.URLField')(max_length=200)),
('name', self.gf('django.db.models.fields.CharField')(max_length=40)),
('authorize_url', self.gf('django.db.models.fields.URLField')(max_length=200)),
('consumer_key', self.gf('django.db.models.fields.CharField')(max_length=80)),
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('access_token_url', self.gf('django.db.models.fields.URLField')(max_length=200)),
('site', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['sites.Site'])),
))
db.send_create_signal('twitter', ['TwitterApp'])
models = {
}
complete_apps = ['twitter']

View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View File

@ -0,0 +1,46 @@
from django.core.urlresolvers import reverse
from django.utils.http import urlencode
from allauth.socialaccount import providers
from allauth.socialaccount.providers.base import Provider, ProviderAccount
class TwitterAccount(ProviderAccount):
def get_screen_name(self):
return self.account.extra_data.get('screen_name')
def get_profile_url(self):
ret = None
screen_name = self.get_screen_name()
if screen_name:
ret = 'http://twitter.com/' + screen_name
return ret
def get_avatar_url(self):
ret = None
profile_image_url = self.account.extra_data.get('profile_image_url')
if profile_image_url:
# Hmm, hack to get our hands on the large image. Not
# really documented, but seems to work.
ret = profile_image_url.replace('_normal', '')
return ret
def __unicode__(self):
screen_name = self.get_screen_name()
return screen_name or super(TwitterAccount, self).__unicode__()
class TwitterProvider(Provider):
id = 'twitter'
name = 'Twitter'
package = 'allauth.socialaccount.providers.twitter'
account_class = TwitterAccount
def get_login_url(self, request, **kwargs):
url = reverse('twitter_login')
if kwargs:
url = url + '?' + urlencode(kwargs)
return url
providers.registry.register(TwitterProvider)

View File

@ -0,0 +1,4 @@
from allauth.socialaccount.providers.oauth.urls import default_urlpatterns
from provider import TwitterProvider
urlpatterns = default_urlpatterns(TwitterProvider)

View File

@ -0,0 +1,47 @@
from django.utils import simplejson
from django.contrib.auth.models import User
from allauth.socialaccount.providers.oauth.client import OAuth
from allauth.socialaccount.providers.oauth.views import (OAuthAdapter,
OAuthLoginView,
OAuthCallbackView)
from allauth.socialaccount.models import SocialLogin, SocialAccount
from provider import TwitterProvider
class TwitterAPI(OAuth):
"""
Verifying twitter credentials
"""
url = 'https://twitter.com/account/verify_credentials.json'
def get_user_info(self):
user = simplejson.loads(self.query(self.url))
return user
class TwitterOAuthAdapter(OAuthAdapter):
provider_id = TwitterProvider.id
request_token_url = 'https://api.twitter.com/oauth/request_token'
access_token_url = 'https://api.twitter.com/oauth/access_token'
# Issue #42 -- this one authenticates over and over again...
# authorize_url = 'https://api.twitter.com/oauth/authorize'
authorize_url = 'https://api.twitter.com/oauth/authenticate'
def complete_login(self, request, app, token):
client = TwitterAPI(request, app.key, app.secret,
self.request_token_url)
extra_data = client.get_user_info()
uid = extra_data['id']
user = User(username=extra_data['screen_name'])
account = SocialAccount(user=user,
uid=uid,
provider=TwitterProvider.id,
extra_data=extra_data)
return SocialLogin(account)
oauth_login = OAuthLoginView.adapter_view(TwitterOAuthAdapter)
oauth_callback = OAuthCallbackView.adapter_view(TwitterOAuthAdapter)

View File

@ -0,0 +1,57 @@
import urllib
import httplib2
class Response(object):
def __init__(self, status_code, content, headers={}):
self.status_code = status_code
self.content = content
self.headers = headers
@classmethod
def from_httplib(cls, resp, content):
return Response(resp.status,
content,
headers=dict(resp.iteritems()))
@property
def json(self):
import json
return json.loads(self.content)
_mocked_responses = []
def mock_next_request(resp):
global _mocked_responses
_mocked_responses.append(resp)
def _mockable_request(f):
def new_f(*args, **kwargs):
global _mocked_responses
if _mocked_responses:
return _mocked_responses.pop(0)
return f(*args, **kwargs)
return new_f
@_mockable_request
def get(url, params={}):
global _mocked_responses
if _mocked_responses:
return _mocked_responses.pop(0)
client = httplib2.Http()
query = urllib.urlencode(params)
if query:
url += '?' + query
resp, content = client.request(url, 'GET')
return Response.from_httplib(resp, content)
@_mockable_request
def post(url, params):
client = httplib2.Http()
headers = { 'content-type': 'application/x-www-form-urlencoded' }
resp, content = client.request(url, 'POST',
body=urllib.urlencode(params),
headers=headers)
return Response.from_httplib(resp, content)

Some files were not shown because too many files have changed in this diff Show More