allauth folder added
This commit is contained in:
parent
efa48df80b
commit
6bfa53f1dc
0
itf/allauth/__init__.py
Normal file
0
itf/allauth/__init__.py
Normal file
0
itf/allauth/account/__init__.py
Normal file
0
itf/allauth/account/__init__.py
Normal file
8
itf/allauth/account/admin.py
Normal file
8
itf/allauth/account/admin.py
Normal 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)
|
57
itf/allauth/account/app_settings.py
Normal file
57
itf/allauth/account/app_settings.py
Normal 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
|
40
itf/allauth/account/auth_backends.py
Normal file
40
itf/allauth/account/auth_backends.py
Normal 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
|
6
itf/allauth/account/context_processors.py
Normal file
6
itf/allauth/account/context_processors.py
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
def account(request):
|
||||||
|
return {
|
||||||
|
"CONTACT_EMAIL": getattr(settings, "CONTACT_EMAIL", "support@example.com")
|
||||||
|
}
|
38
itf/allauth/account/decorators.py
Normal file
38
itf/allauth/account/decorators.py
Normal 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
|
479
itf/allauth/account/forms.py
Normal file
479
itf/allauth/account/forms.py
Normal 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)
|
||||||
|
|
||||||
|
|
11
itf/allauth/account/models.py
Normal file
11
itf/allauth/account/models.py
Normal 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)
|
4
itf/allauth/account/signals.py
Normal file
4
itf/allauth/account/signals.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
import django.dispatch
|
||||||
|
|
||||||
|
|
||||||
|
user_logged_in = django.dispatch.Signal(providing_args=["request", "user"])
|
1
itf/allauth/account/templatetags/__init__.py
Normal file
1
itf/allauth/account/templatetags/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
47
itf/allauth/account/templatetags/account_tags.py
Normal file
47
itf/allauth/account/templatetags/account_tags.py
Normal 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)
|
98
itf/allauth/account/tests.py
Normal file
98
itf/allauth/account/tests.py
Normal 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
|
||||||
|
|
24
itf/allauth/account/urls.py
Normal file
24
itf/allauth/account/urls.py
Normal 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"),
|
||||||
|
|
||||||
|
)
|
162
itf/allauth/account/utils.py
Normal file
162
itf/allauth/account/utils.py
Normal 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)
|
||||||
|
|
250
itf/allauth/account/views.py
Normal file
250
itf/allauth/account/views.py
Normal 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)
|
10
itf/allauth/app_settings.py
Normal file
10
itf/allauth/app_settings.py
Normal 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', '/')
|
||||||
|
|
BIN
itf/allauth/locale/nl/LC_MESSAGES/django.mo
Normal file
BIN
itf/allauth/locale/nl/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
689
itf/allauth/locale/nl/LC_MESSAGES/django.po
Normal file
689
itf/allauth/locale/nl/LC_MESSAGES/django.po
Normal 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 »"
|
||||||
|
#~ msgstr "Inloggen"
|
0
itf/allauth/models.py
Normal file
0
itf/allauth/models.py
Normal file
0
itf/allauth/socialaccount/__init__.py
Normal file
0
itf/allauth/socialaccount/__init__.py
Normal file
21
itf/allauth/socialaccount/admin.py
Normal file
21
itf/allauth/socialaccount/admin.py
Normal 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)
|
21
itf/allauth/socialaccount/app_settings.py
Normal file
21
itf/allauth/socialaccount/app_settings.py
Normal 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", {})
|
5
itf/allauth/socialaccount/context_processors.py
Normal file
5
itf/allauth/socialaccount/context_processors.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import providers
|
||||||
|
|
||||||
|
def socialaccount(request):
|
||||||
|
ctx = { 'providers': providers.registry.get_list() }
|
||||||
|
return dict(socialaccount=ctx)
|
60
itf/allauth/socialaccount/fields.py
Normal file
60
itf/allauth/socialaccount/fields.py
Normal 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
|
55
itf/allauth/socialaccount/forms.py
Normal file
55
itf/allauth/socialaccount/forms.py
Normal 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()
|
179
itf/allauth/socialaccount/helpers.py
Normal file
179
itf/allauth/socialaccount/helpers.py
Normal 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)
|
73
itf/allauth/socialaccount/migrations/0001_initial.py
Normal file
73
itf/allauth/socialaccount/migrations/0001_initial.py
Normal 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']
|
150
itf/allauth/socialaccount/migrations/0002_genericmodels.py
Normal file
150
itf/allauth/socialaccount/migrations/0002_genericmodels.py
Normal 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']
|
|
@ -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']
|
0
itf/allauth/socialaccount/migrations/__init__.py
Normal file
0
itf/allauth/socialaccount/migrations/__init__.py
Normal file
197
itf/allauth/socialaccount/models.py
Normal file
197
itf/allauth/socialaccount/models.py
Normal 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
|
||||||
|
|
||||||
|
|
36
itf/allauth/socialaccount/providers/__init__.py
Normal file
36
itf/allauth/socialaccount/providers/__init__.py
Normal 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()
|
52
itf/allauth/socialaccount/providers/base.py
Normal file
52
itf/allauth/socialaccount/providers/base.py
Normal 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']
|
|
@ -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>
|
5
itf/allauth/socialaccount/providers/facebook/forms.py
Normal file
5
itf/allauth/socialaccount/providers/facebook/forms.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
from django import forms
|
||||||
|
|
||||||
|
|
||||||
|
class FacebookConnectForm(forms.Form):
|
||||||
|
access_token = forms.CharField(required=True)
|
70
itf/allauth/socialaccount/providers/facebook/locale.py
Normal file
70
itf/allauth/socialaccount/providers/facebook/locale.py
Normal 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
|
|
@ -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']
|
|
@ -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']
|
|
@ -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']
|
|
@ -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']
|
3
itf/allauth/socialaccount/providers/facebook/models.py
Normal file
3
itf/allauth/socialaccount/providers/facebook/models.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
92
itf/allauth/socialaccount/providers/facebook/provider.py
Normal file
92
itf/allauth/socialaccount/providers/facebook/provider.py
Normal 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)
|
|
@ -0,0 +1 @@
|
||||||
|
<script src="//connect.facebook.net/{{facebook_jssdk_locale}}/all.js"></script>
|
|
@ -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>
|
14
itf/allauth/socialaccount/providers/facebook/urls.py
Normal file
14
itf/allauth/socialaccount/providers/facebook/urls.py
Normal 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'),
|
||||||
|
)
|
83
itf/allauth/socialaccount/providers/facebook/views.py
Normal file
83
itf/allauth/socialaccount/providers/facebook/views.py
Normal 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
|
3
itf/allauth/socialaccount/providers/github/models.py
Normal file
3
itf/allauth/socialaccount/providers/github/models.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
24
itf/allauth/socialaccount/providers/github/provider.py
Normal file
24
itf/allauth/socialaccount/providers/github/provider.py
Normal 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)
|
5
itf/allauth/socialaccount/providers/github/urls.py
Normal file
5
itf/allauth/socialaccount/providers/github/urls.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns
|
||||||
|
from provider import GitHubProvider
|
||||||
|
|
||||||
|
urlpatterns = default_urlpatterns(GitHubProvider)
|
||||||
|
|
35
itf/allauth/socialaccount/providers/github/views.py
Normal file
35
itf/allauth/socialaccount/providers/github/views.py
Normal 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)
|
||||||
|
|
3
itf/allauth/socialaccount/providers/google/models.py
Normal file
3
itf/allauth/socialaccount/providers/google/models.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
35
itf/allauth/socialaccount/providers/google/provider.py
Normal file
35
itf/allauth/socialaccount/providers/google/provider.py
Normal 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)
|
4
itf/allauth/socialaccount/providers/google/urls.py
Normal file
4
itf/allauth/socialaccount/providers/google/urls.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns
|
||||||
|
from provider import GoogleProvider
|
||||||
|
|
||||||
|
urlpatterns = default_urlpatterns(GoogleProvider)
|
46
itf/allauth/socialaccount/providers/google/views.py
Normal file
46
itf/allauth/socialaccount/providers/google/views.py
Normal 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)
|
||||||
|
|
3
itf/allauth/socialaccount/providers/linkedin/models.py
Normal file
3
itf/allauth/socialaccount/providers/linkedin/models.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
15
itf/allauth/socialaccount/providers/linkedin/provider.py
Normal file
15
itf/allauth/socialaccount/providers/linkedin/provider.py
Normal 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)
|
4
itf/allauth/socialaccount/providers/linkedin/urls.py
Normal file
4
itf/allauth/socialaccount/providers/linkedin/urls.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
from allauth.socialaccount.providers.oauth.urls import default_urlpatterns
|
||||||
|
from provider import LinkedInProvider
|
||||||
|
|
||||||
|
urlpatterns = default_urlpatterns(LinkedInProvider)
|
68
itf/allauth/socialaccount/providers/linkedin/views.py
Normal file
68
itf/allauth/socialaccount/providers/linkedin/views.py
Normal 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)
|
||||||
|
|
185
itf/allauth/socialaccount/providers/oauth/client.py
Normal file
185
itf/allauth/socialaccount/providers/oauth/client.py
Normal 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
|
3
itf/allauth/socialaccount/providers/oauth/models.py
Normal file
3
itf/allauth/socialaccount/providers/oauth/models.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
13
itf/allauth/socialaccount/providers/oauth/provider.py
Normal file
13
itf/allauth/socialaccount/providers/oauth/provider.py
Normal 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
|
||||||
|
|
||||||
|
|
12
itf/allauth/socialaccount/providers/oauth/urls.py
Normal file
12
itf/allauth/socialaccount/providers/oauth/urls.py
Normal 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)))
|
84
itf/allauth/socialaccount/providers/oauth/views.py
Normal file
84
itf/allauth/socialaccount/providers/oauth/views.py
Normal 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)
|
58
itf/allauth/socialaccount/providers/oauth2/client.py
Normal file
58
itf/allauth/socialaccount/providers/oauth2/client.py
Normal 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
|
3
itf/allauth/socialaccount/providers/oauth2/models.py
Normal file
3
itf/allauth/socialaccount/providers/oauth2/models.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
23
itf/allauth/socialaccount/providers/oauth2/provider.py
Normal file
23
itf/allauth/socialaccount/providers/oauth2/provider.py
Normal 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 []
|
||||||
|
|
||||||
|
|
11
itf/allauth/socialaccount/providers/oauth2/urls.py
Normal file
11
itf/allauth/socialaccount/providers/oauth2/urls.py
Normal 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)))
|
76
itf/allauth/socialaccount/providers/oauth2/views.py
Normal file
76
itf/allauth/socialaccount/providers/oauth2/views.py
Normal 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)
|
||||||
|
|
13
itf/allauth/socialaccount/providers/openid/admin.py
Normal file
13
itf/allauth/socialaccount/providers/openid/admin.py
Normal 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)
|
7
itf/allauth/socialaccount/providers/openid/forms.py
Normal file
7
itf/allauth/socialaccount/providers/openid/forms.py
Normal 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>')
|
|
@ -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']
|
|
@ -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']
|
|
@ -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']
|
22
itf/allauth/socialaccount/providers/openid/models.py
Normal file
22
itf/allauth/socialaccount/providers/openid/models.py
Normal 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
|
58
itf/allauth/socialaccount/providers/openid/provider.py
Normal file
58
itf/allauth/socialaccount/providers/openid/provider.py
Normal 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)
|
16
itf/allauth/socialaccount/providers/openid/tests.py
Normal file
16
itf/allauth/socialaccount/providers/openid/tests.py
Normal 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'))
|
8
itf/allauth/socialaccount/providers/openid/urls.py
Normal file
8
itf/allauth/socialaccount/providers/openid/urls.py
Normal 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),
|
||||||
|
)
|
76
itf/allauth/socialaccount/providers/openid/utils.py
Normal file
76
itf/allauth/socialaccount/providers/openid/utils.py
Normal 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
|
109
itf/allauth/socialaccount/providers/openid/views.py
Normal file
109
itf/allauth/socialaccount/providers/openid/views.py
Normal 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
|
||||||
|
|
|
@ -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']
|
|
@ -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']
|
|
@ -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']
|
|
@ -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']
|
3
itf/allauth/socialaccount/providers/twitter/models.py
Normal file
3
itf/allauth/socialaccount/providers/twitter/models.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
46
itf/allauth/socialaccount/providers/twitter/provider.py
Normal file
46
itf/allauth/socialaccount/providers/twitter/provider.py
Normal 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)
|
4
itf/allauth/socialaccount/providers/twitter/urls.py
Normal file
4
itf/allauth/socialaccount/providers/twitter/urls.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
from allauth.socialaccount.providers.oauth.urls import default_urlpatterns
|
||||||
|
from provider import TwitterProvider
|
||||||
|
|
||||||
|
urlpatterns = default_urlpatterns(TwitterProvider)
|
47
itf/allauth/socialaccount/providers/twitter/views.py
Normal file
47
itf/allauth/socialaccount/providers/twitter/views.py
Normal 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)
|
||||||
|
|
57
itf/allauth/socialaccount/requests.py
Normal file
57
itf/allauth/socialaccount/requests.py
Normal 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
Loading…
Reference in New Issue
Block a user