Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
e89afa93
Commit
e89afa93
authored
Oct 14, 2014
by
Will Daly
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
WIP: add login and registration end-points to the user API.
parent
80613361
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
663 additions
and
17 deletions
+663
-17
common/djangoapps/user_api/api/profile.py
+9
-3
common/djangoapps/user_api/helpers.py
+241
-0
common/djangoapps/user_api/tests/test_views.py
+0
-0
common/djangoapps/user_api/urls.py
+2
-0
common/djangoapps/user_api/views.py
+258
-3
lms/djangoapps/student_account/test/test_views.py
+32
-0
lms/djangoapps/student_account/urls.py
+11
-1
lms/djangoapps/student_account/views.py
+35
-2
lms/djangoapps/student_profile/urls.py
+8
-2
lms/envs/test.py
+11
-0
lms/templates/student_account/login_and_register.html
+52
-0
lms/urls.py
+4
-6
No files found.
common/djangoapps/user_api/api/profile.py
View file @
e89afa93
...
...
@@ -65,9 +65,15 @@ def profile_info(username):
return
None
profile_dict
=
{
u'username'
:
profile
.
user
.
username
,
u'email'
:
profile
.
user
.
email
,
u'full_name'
:
profile
.
name
,
"username"
:
profile
.
user
.
username
,
"email"
:
profile
.
user
.
email
,
"full_name"
:
profile
.
name
,
"level_of_education"
:
profile
.
level_of_education
,
"mailing_address"
:
profile
.
mailing_address
,
"year_of_birth"
:
profile
.
year_of_birth
,
"goals"
:
profile
.
goals
,
"city"
:
profile
.
city
,
"country"
:
profile
.
country
,
}
return
profile_dict
...
...
common/djangoapps/user_api/helpers.py
View file @
e89afa93
...
...
@@ -4,6 +4,8 @@ This is NOT part of the public API.
"""
from
functools
import
wraps
import
logging
import
json
LOGGER
=
logging
.
getLogger
(
__name__
)
...
...
@@ -54,3 +56,242 @@ def intercept_errors(api_error, ignore_errors=[]):
raise
api_error
(
msg
)
return
_wrapped
return
_decorator
class
InvalidFieldError
(
Exception
):
"""The provided field definition is not valid. """
class
FormDescription
(
object
):
"""Generate a JSON representation of a form. """
ALLOWED_TYPES
=
[
"text"
,
"select"
,
"textarea"
]
ALLOWED_RESTRICTIONS
=
{
"text"
:
[
"min_length"
,
"max_length"
],
}
def
__init__
(
self
,
method
,
submit_url
):
"""Configure how the form should be submitted.
Args:
method (unicode): The HTTP method used to submit the form.
submit_url (unicode): The URL where the form should be submitted.
"""
self
.
method
=
method
self
.
submit_url
=
submit_url
self
.
fields
=
[]
def
add_field
(
self
,
name
,
label
=
u""
,
field_type
=
u"text"
,
default
=
u""
,
placeholder
=
u""
,
instructions
=
u""
,
required
=
True
,
restrictions
=
None
,
options
=
None
):
"""Add a field to the form description.
Args:
name (unicode): The name of the field, which is the key for the value
to send back to the server.
Keyword Arguments:
label (unicode): The label for the field (e.g. "E-mail" or "Username")
field_type (unicode): The type of the field. See `ALLOWED_TYPES` for
acceptable values.
default (unicode): The default value for the field.
placeholder (unicode): Placeholder text in the field
(e.g. "user@example.com" for an email field)
instructions (unicode): Short instructions for using the field
(e.g. "This is the email address you used when you registered.")
required (boolean): Whether the field is required or optional.
restrictions (dict): Validation restrictions for the field.
See `ALLOWED_RESTRICTIONS` for acceptable values.
options (list): For "select" fields, a list of tuples
(value, display_name) representing the options available to
the user. `value` is the value of the field to send to the server,
and `display_name` is the name to display to the user.
If the field type is "select", you *must* provide this kwarg.
Raises:
InvalidFieldError
"""
if
field_type
not
in
self
.
ALLOWED_TYPES
:
msg
=
u"Field type '{field_type}' is not a valid type. Allowed types are: {allowed}."
.
format
(
field_type
=
field_type
,
allowed
=
", "
.
join
(
self
.
ALLOWED_TYPES
)
)
raise
InvalidFieldError
(
msg
)
field_dict
=
{
"label"
:
label
,
"name"
:
name
,
"type"
:
field_type
,
"default"
:
default
,
"placeholder"
:
placeholder
,
"instructions"
:
instructions
,
"required"
:
required
,
"restrictions"
:
{}
}
if
field_type
==
"select"
:
if
options
is
not
None
:
field_dict
[
"options"
]
=
[
{
"value"
:
option_value
,
"name"
:
option_name
}
for
option_value
,
option_name
in
options
]
else
:
raise
InvalidFieldError
(
"You must provide options for a select field."
)
if
restrictions
is
not
None
:
allowed_restrictions
=
self
.
ALLOWED_RESTRICTIONS
.
get
(
field_type
,
[])
for
key
,
val
in
restrictions
.
iteritems
():
if
key
in
allowed_restrictions
:
field_dict
[
"restrictions"
][
key
]
=
val
else
:
msg
=
"Restriction '{restriction}' is not allowed for field type '{field_type}'"
.
format
(
restriction
=
key
,
field_type
=
field_type
)
raise
InvalidFieldError
(
msg
)
self
.
fields
.
append
(
field_dict
)
def
to_json
(
self
):
"""Create a JSON representation of the form description.
Here's an example of the output:
{
"method": "post",
"submit_url": "/submit",
"fields": [
{
"name": "cheese_or_wine",
"label": "Cheese or Wine?",
"default": "cheese",
"type": "select",
"required": True,
"placeholder": "",
"instructions": "",
"options": [
{"value": "cheese", "name": "Cheese"},
{"value": "wine", "name": "Wine"}
]
"restrictions": {},
},
{
"name": "comments",
"label": "comments",
"default": "",
"type": "text",
"required": False,
"placeholder": "Any comments?",
"instructions": "Please enter additional comments here."
"restrictions": {
"max_length": 200
}
},
...
]
}
If the field is NOT a "select" type, then the "options"
key will be omitted.
Returns:
unicode
"""
return
json
.
dumps
({
"method"
:
self
.
method
,
"submit_url"
:
self
.
submit_url
,
"fields"
:
self
.
fields
})
def
shim_student_view
(
view_func
,
check_logged_in
=
False
):
"""Create a "shim" view for a view function from the student Django app.
Specifically, we need to:
* Strip out enrollment params, since the client for the new registration/login
page will communicate with the enrollment API to update enrollments.
* Return responses with HTTP status codes indicating success/failure
(instead of always using status 200, but setting "success" to False in
the JSON-serialized content of the response)
* Use status code 302 for redirects instead of
"redirect_url" in the JSON-serialized content of the response.
* Use status code 403 to indicate a login failure.
The shim will preserve any cookies set by the view.
Arguments:
view_func (function): The view function from the student Django app.
Keyword Args:
check_logged_in (boolean): If true, check whether the user successfully
authenticated and if not set the status to 403.
Returns:
function
"""
@wraps
(
view_func
)
def
_inner
(
request
):
# Strip out enrollment action stuff, since we're handling that elsewhere
if
"enrollment_action"
in
request
.
POST
:
del
request
.
POST
[
"enrollment_action"
]
if
"course_id"
in
request
.
POST
:
del
request
.
POST
[
"course_id"
]
# Actually call the function!
# TODO ^^
response
=
view_func
(
request
)
# Most responses from this view are a JSON dict
# TODO -- explain this more
try
:
response_dict
=
json
.
loads
(
response
.
content
)
msg
=
response_dict
.
get
(
"value"
,
u""
)
redirect_url
=
response_dict
.
get
(
"redirect_url"
)
except
(
ValueError
,
TypeError
):
msg
=
response
.
content
redirect_url
=
None
# If the user could not be authenticated
if
check_logged_in
and
not
request
.
user
.
is_authenticated
():
response
.
status_code
=
403
response
.
content
=
msg
# Handle redirects
# TODO -- explain why this is safe
elif
redirect_url
is
not
None
:
response
.
status_code
=
302
response
.
content
=
redirect_url
# Handle errors
elif
response
.
status_code
!=
200
or
not
response_dict
.
get
(
"success"
,
False
):
# TODO -- explain this
if
response
.
status_code
==
200
:
response
.
status_code
=
400
response
.
content
=
msg
# Otherwise, return the response
else
:
response
.
content
=
msg
# Return the response.
# IMPORTANT: this NEEDS to preserve session variables / cookies!
return
response
return
_inner
common/djangoapps/user_api/tests/test_views.py
View file @
e89afa93
This diff is collapsed.
Click to expand it.
common/djangoapps/user_api/urls.py
View file @
e89afa93
...
...
@@ -10,6 +10,8 @@ user_api_router.register(r'user_prefs', user_api_views.UserPreferenceViewSet)
urlpatterns
=
patterns
(
''
,
url
(
r'^v1/'
,
include
(
user_api_router
.
urls
)),
url
(
r'^v1/account/login_session/$'
,
user_api_views
.
LoginSessionView
.
as_view
(),
name
=
"user_api_login_session"
),
url
(
r'^v1/account/registration/$'
,
user_api_views
.
RegistrationView
.
as_view
(),
name
=
"user_api_registration"
),
url
(
r'^v1/preferences/(?P<pref_key>{})/users/$'
.
format
(
UserPreference
.
KEY_REGEX
),
user_api_views
.
PreferenceUsersListView
.
as_view
()
...
...
common/djangoapps/user_api/views.py
View file @
e89afa93
"""TODO"""
from
django.conf
import
settings
from
django.contrib.auth.models
import
User
from
django.http
import
HttpResponse
,
HttpResponseBadRequest
from
django.core.urlresolvers
import
reverse
from
django.utils.translation
import
ugettext
as
_
from
django.utils.decorators
import
method_decorator
from
django.views.decorators.csrf
import
ensure_csrf_cookie
from
rest_framework
import
authentication
from
rest_framework
import
filters
from
rest_framework
import
generics
from
rest_framework
import
permissions
from
rest_framework
import
status
from
rest_framework
import
viewsets
from
rest_framework.views
import
APIView
from
rest_framework.exceptions
import
ParseError
from
rest_framework.response
import
Response
from
django_countries.countries
import
COUNTRIES
from
user_api.serializers
import
UserSerializer
,
UserPreferenceSerializer
from
user_api.models
import
UserPreference
from
user_api.models
import
UserPreference
,
UserProfile
from
django_comment_common.models
import
Role
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
from
user_api.api
import
account
as
account_api
,
profile
as
profile_api
from
user_api.helpers
import
FormDescription
,
shim_student_view
class
ApiKeyHeaderPermission
(
permissions
.
BasePermission
):
def
has_permission
(
self
,
request
,
view
):
...
...
@@ -31,6 +41,251 @@ class ApiKeyHeaderPermission(permissions.BasePermission):
)
class
LoginSessionView
(
APIView
):
"""TODO"""
def
get
(
self
,
request
):
"""Render a form for allowing a user to log in.
TODO
"""
form_desc
=
FormDescription
(
"post"
,
reverse
(
"user_api_login_session"
))
form_desc
.
add_field
(
"email"
,
label
=
_
(
u"E-mail"
),
placeholder
=
_
(
u"example: username@domain.com"
),
instructions
=
_
(
u"This is the e-mail address you used to register with {platform}"
)
.
format
(
platform
=
settings
.
PLATFORM_NAME
),
restrictions
=
{
"min_length"
:
account_api
.
EMAIL_MIN_LENGTH
,
"max_length"
:
account_api
.
EMAIL_MAX_LENGTH
,
}
)
form_desc
.
add_field
(
"password"
,
label
=
_
(
u"Password"
),
restrictions
=
{
"min_length"
:
account_api
.
PASSWORD_MIN_LENGTH
,
"max_length"
:
account_api
.
PASSWORD_MAX_LENGTH
,
}
)
return
HttpResponse
(
form_desc
.
to_json
(),
content_type
=
"application/json"
)
@method_decorator
(
ensure_csrf_cookie
)
def
post
(
self
,
request
):
"""Authenticate a user and log them in.
TODO
"""
# Validate the parameters
# If either param is missing, it's a malformed request
email
=
request
.
POST
.
get
(
"email"
)
password
=
request
.
POST
.
get
(
"password"
)
if
email
is
None
or
password
is
None
:
return
HttpResponseBadRequest
()
return
self
.
_login_shim
(
request
)
def
_login_shim
(
self
,
request
):
# Initially, this should be a shim to student views,
# since it will be too much work to re-implement everything there.
# Eventually, we'll want to pull out that functionality into this Django app.
from
student.views
import
login_user
return
shim_student_view
(
login_user
,
check_logged_in
=
True
)(
request
)
class
RegistrationView
(
APIView
):
"""TODO"""
DEFAULT_FIELDS
=
[
"email"
,
"name"
,
"username"
,
"password"
]
EXTRA_FIELDS
=
[
"city"
,
"country"
,
"level_of_education"
,
"gender"
,
"year_of_birth"
,
"mailing_address"
,
"goals"
,
]
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
(
RegistrationView
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
self
.
field_handlers
=
{}
for
field_name
in
(
self
.
DEFAULT_FIELDS
+
self
.
EXTRA_FIELDS
):
handler
=
getattr
(
self
,
"_add_{field_name}_field"
.
format
(
field_name
=
field_name
))
self
.
field_handlers
[
field_name
]
=
handler
def
get
(
self
,
request
):
"""Render a form for allowing the user to register.
TODO
"""
form_desc
=
FormDescription
(
"post"
,
reverse
(
"user_api_registration"
))
# Default fields are always required
for
field_name
in
self
.
DEFAULT_FIELDS
:
self
.
field_handlers
[
field_name
](
form_desc
,
required
=
True
)
# Extra fields from configuration may be required, optional, or hidden
# TODO -- explain error handling here
for
field_name
in
self
.
EXTRA_FIELDS
:
field_setting
=
settings
.
REGISTRATION_EXTRA_FIELDS
.
get
(
field_name
)
handler
=
self
.
field_handlers
[
field_name
]
if
field_setting
in
[
"required"
,
"optional"
]:
handler
(
form_desc
,
required
=
(
field_setting
==
"required"
))
elif
field_setting
!=
"hidden"
:
# TODO -- warning here
pass
return
HttpResponse
(
form_desc
.
to_json
(),
content_type
=
"application/json"
)
def
post
(
self
,
request
):
"""Create the user's account.
TODO
"""
# Backwards compat:
# TODO -- explain this
request
.
POST
[
"honor_code"
]
=
"true"
request
.
POST
[
"terms_of_service"
]
=
"true"
# Initially, this should be a shim to student views.
# Eventually, we'll want to pull that functionality into this API.
from
student.views
import
create_account
return
shim_student_view
(
create_account
)(
request
)
def
_add_email_field
(
self
,
form_desc
,
required
=
True
):
"""TODO """
form_desc
.
add_field
(
"email"
,
label
=
_
(
u"E-mail"
),
placeholder
=
_
(
u"example: username@domain.com"
),
instructions
=
_
(
u"This is the e-mail address you used to register with {platform}"
)
.
format
(
platform
=
settings
.
PLATFORM_NAME
),
restrictions
=
{
"min_length"
:
account_api
.
EMAIL_MIN_LENGTH
,
"max_length"
:
account_api
.
EMAIL_MAX_LENGTH
,
},
required
=
required
)
def
_add_name_field
(
self
,
form_desc
,
required
=
True
):
"""TODO"""
form_desc
.
add_field
(
"name"
,
label
=
_
(
u"Full Name"
),
instructions
=
_
(
u"Needed for any certificates you may earn"
),
restrictions
=
{
"max_length"
:
profile_api
.
FULL_NAME_MAX_LENGTH
,
},
required
=
required
)
def
_add_username_field
(
self
,
form_desc
,
required
=
True
):
"""TODO"""
form_desc
.
add_field
(
"username"
,
label
=
_
(
u"Public Username"
),
instructions
=
_
(
u"Will be shown in any discussions or forums you participate in (cannot be changed)"
),
restrictions
=
{
"min_length"
:
account_api
.
USERNAME_MIN_LENGTH
,
"max_length"
:
account_api
.
USERNAME_MAX_LENGTH
,
},
required
=
required
)
def
_add_password_field
(
self
,
form_desc
,
required
=
True
):
"""TODO"""
form_desc
.
add_field
(
"password"
,
label
=
_
(
u"Password"
),
restrictions
=
{
"min_length"
:
account_api
.
PASSWORD_MIN_LENGTH
,
"max_length"
:
account_api
.
PASSWORD_MAX_LENGTH
,
},
required
=
required
)
def
_add_level_of_education_field
(
self
,
form_desc
,
required
=
True
):
""" TODO """
form_desc
.
add_field
(
"level_of_education"
,
label
=
_
(
"Highest Level of Education Completed"
),
field_type
=
"select"
,
options
=
self
.
_options_with_default
(
UserProfile
.
LEVEL_OF_EDUCATION_CHOICES
),
required
=
required
)
def
_add_gender_field
(
self
,
form_desc
,
required
=
True
):
"""TODO """
form_desc
.
add_field
(
"gender"
,
label
=
_
(
"Gender"
),
field_type
=
"select"
,
options
=
self
.
_options_with_default
(
UserProfile
.
GENDER_CHOICES
),
required
=
required
)
def
_add_year_of_birth_field
(
self
,
form_desc
,
required
=
True
):
"""TODO """
options
=
[(
unicode
(
year
),
unicode
(
year
))
for
year
in
UserProfile
.
VALID_YEARS
]
form_desc
.
add_field
(
"year_of_birth"
,
label
=
_
(
"Year of Birth"
),
field_type
=
"select"
,
options
=
self
.
_options_with_default
(
options
),
required
=
required
)
def
_add_mailing_address_field
(
self
,
form_desc
,
required
=
True
):
"""TODO """
form_desc
.
add_field
(
"mailing_address"
,
label
=
_
(
"Mailing Address"
),
field_type
=
"textarea"
,
required
=
required
)
def
_add_goals_field
(
self
,
form_desc
,
required
=
True
):
"""TODO """
form_desc
.
add_field
(
"goals"
,
label
=
_
(
"Please share with us your reasons for registering with edX"
),
field_type
=
"textarea"
,
required
=
required
)
def
_add_city_field
(
self
,
form_desc
,
required
=
True
):
"""TODO """
form_desc
.
add_field
(
"city"
,
label
=
_
(
"City"
),
required
=
required
)
def
_add_country_field
(
self
,
form_desc
,
required
=
True
):
"""TODO """
options
=
[
(
country_code
,
unicode
(
country_name
))
for
country_code
,
country_name
in
COUNTRIES
]
form_desc
.
add_field
(
"country"
,
label
=
_
(
"Country"
),
field_type
=
"select"
,
options
=
self
.
_options_with_default
(
options
),
required
=
required
)
def
_options_with_default
(
self
,
options
):
"""TODO """
return
(
[(
""
,
"--"
)]
+
list
(
options
)
)
class
UserViewSet
(
viewsets
.
ReadOnlyModelViewSet
):
authentication_classes
=
(
authentication
.
SessionAuthentication
,)
permission_classes
=
(
ApiKeyHeaderPermission
,)
...
...
lms/djangoapps/student_account/test/test_views.py
View file @
e89afa93
...
...
@@ -3,6 +3,7 @@
import
re
from
urllib
import
urlencode
import
json
from
mock
import
patch
import
ddt
from
django.test
import
TestCase
...
...
@@ -60,6 +61,37 @@ class StudentAccountViewTest(UrlResetMixin, TestCase):
response
=
self
.
client
.
get
(
reverse
(
'account_index'
))
self
.
assertContains
(
response
,
"Student Account"
)
@ddt.data
(
(
"login"
,
"login"
),
(
"register"
,
"register"
),
)
@ddt.unpack
def
test_login_and_registration_form
(
self
,
url_name
,
initial_mode
):
response
=
self
.
client
.
get
(
reverse
(
url_name
))
expected_data
=
u"data-initial-mode=
\"
{mode}
\"
"
.
format
(
mode
=
initial_mode
)
self
.
assertContains
(
response
,
expected_data
)
@ddt.data
(
"login"
,
"register"
)
def
test_login_and_registration_third_party_auth_urls
(
self
,
url_name
):
response
=
self
.
client
.
get
(
reverse
(
url_name
))
# This relies on the THIRD_PARTY_AUTH configuration in the test settings
expected_data
=
u"data-third-party-auth-providers=
\"
{providers}
\"
"
.
format
(
providers
=
json
.
dumps
([
{
u'icon_class'
:
u'icon-facebook'
,
u'login_url'
:
u'/auth/login/facebook/?auth_entry=login'
,
u'name'
:
u'Facebook'
},
{
u'icon_class'
:
u'icon-google-plus'
,
u'login_url'
:
u'/auth/login/google-oauth2/?auth_entry=login'
,
u'name'
:
u'Google'
}
])
)
self
.
assertContains
(
response
,
expected_data
)
def
test_change_email
(
self
):
response
=
self
.
_change_email
(
self
.
NEW_EMAIL
,
self
.
PASSWORD
)
self
.
assertEquals
(
response
.
status_code
,
200
)
...
...
lms/djangoapps/student_account/urls.py
View file @
e89afa93
from
django.conf.urls
import
patterns
,
url
from
django.conf
import
settings
urlpatterns
=
patterns
(
'student_account.views'
,
url
(
r'^login/$'
,
'login_and_registration_form'
,
{
'initial_mode'
:
'login'
},
name
=
'login'
),
url
(
r'^register/$'
,
'login_and_registration_form'
,
{
'initial_mode'
:
'register'
},
name
=
'register'
),
)
if
settings
.
FEATURES
.
get
(
'ENABLE_NEW_DASHBOARD'
):
urlpatterns
+=
patterns
(
'student_account.views'
,
url
(
r'^$'
,
'index'
,
name
=
'account_index'
),
url
(
r'^email$'
,
'email_change_request_handler'
,
name
=
'email_change_request'
),
url
(
r'^email/confirmation/(?P<key>[^/]*)$'
,
'email_change_confirmation_handler'
,
name
=
'email_change_confirm'
),
)
)
\ No newline at end of file
lms/djangoapps/student_account/views.py
View file @
e89afa93
""" Views for a student's account information. """
import
json
from
django.conf
import
settings
from
django.http
import
(
QueryDict
,
HttpResponse
,
HttpResponseBadRequest
,
HttpResponseServerError
HttpResponse
,
HttpResponseBadRequest
,
HttpResponseServerError
)
from
django.core.mail
import
send_mail
from
django_future.csrf
import
ensure_csrf_cookie
from
django.contrib.auth.decorators
import
login_required
from
django.views.decorators.http
import
require_http_methods
from
edxmako.shortcuts
import
render_to_response
,
render_to_string
import
third_party_auth
from
microsite_configuration
import
microsite
from
user_api.api
import
account
as
account_api
...
...
@@ -41,6 +42,38 @@ def index(request):
)
@require_http_methods
([
'GET'
])
def
login_and_registration_form
(
request
,
initial_mode
=
"login"
):
"""Render the combined login/registration form, defaulting to login
This relies on the JS to asynchronously load the actual form from
the user_api.
Keyword Args:
initial_mode (string): Either "login" or "registration".
"""
context
=
{
'disable_courseware_js'
:
True
,
'initial_mode'
:
initial_mode
,
'third_party_auth_providers'
:
json
.
dumps
([])
}
if
microsite
.
get_value
(
"ENABLE_THIRD_PARTY_AUTH"
,
settings
.
FEATURES
.
get
(
"ENABLE_THIRD_PARTY_AUTH"
)):
context
[
"third_party_auth_providers"
]
=
json
.
dumps
([
{
"name"
:
enabled
.
NAME
,
"icon_class"
:
enabled
.
ICON_CLASS
,
"login_url"
:
third_party_auth
.
pipeline
.
get_login_url
(
enabled
.
NAME
,
third_party_auth
.
pipeline
.
AUTH_ENTRY_LOGIN
),
}
for
enabled
in
third_party_auth
.
provider
.
Registry
.
enabled
()
])
return
render_to_response
(
'student_account/login_and_register.html'
,
context
)
@login_required
@require_http_methods
([
'POST'
])
@ensure_csrf_cookie
...
...
lms/djangoapps/student_profile/urls.py
View file @
e89afa93
from
django.conf.urls
import
patterns
,
url
from
django.conf
import
settings
urlpatterns
=
patterns
(
urlpatterns
=
[]
if
settings
.
FEATURES
.
get
(
'ENABLE_NEW_DASHBOARD'
):
urlpatterns
=
patterns
(
'student_profile.views'
,
url
(
r'^$'
,
'index'
,
name
=
'profile_index'
),
url
(
r'^preferences$'
,
'preference_handler'
,
name
=
'preference_handler'
),
url
(
r'^preferences/languages$'
,
'language_info'
,
name
=
'language_info'
),
)
)
lms/envs/test.py
View file @
e89afa93
...
...
@@ -203,6 +203,17 @@ simplefilter('ignore') # Change to "default" to see the first instance of each
######### Third-party auth ##########
FEATURES
[
'ENABLE_THIRD_PARTY_AUTH'
]
=
True
THIRD_PARTY_AUTH
=
{
"Google"
:
{
"SOCIAL_AUTH_GOOGLE_OAUTH2_KEY"
:
"test"
,
"SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET"
:
"test"
,
},
"Facebook"
:
{
"SOCIAL_AUTH_GOOGLE_OAUTH2_KEY"
:
"test"
,
"SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET"
:
"test"
,
},
}
################################## OPENID #####################################
FEATURES
[
'AUTH_USE_OPENID'
]
=
True
FEATURES
[
'AUTH_USE_OPENID_PROVIDER'
]
=
True
...
...
lms/templates/student_account/login_and_register.html
0 → 100644
View file @
e89afa93
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
%
>
<
%
namespace
name=
'static'
file=
'/static_content.html'
/>
<
%
inherit
file=
"../main.html"
/>
<
%
block
name=
"pagetitle"
>
${_("Login and Register")}
</
%
block>
<
%
block
name=
"js_extra"
>
<script
type=
"text/javascript"
src=
"${static.url('js/vendor/underscore-min.js')}"
></script>
<script
type=
"text/javascript"
src=
"${static.url('js/vendor/backbone-min.js')}"
></script>
<
%
static:js
group=
'student_account'
/>
</
%
block>
<
%
block
name=
"header_extras"
>
% for template_name in ["account"]:
<script
type=
"text/template"
id=
"${template_name}-tpl"
>
<%
static
:
include
path
=
"student_account/${template_name}.underscore"
/>
</script>
% endfor
</
%
block>
<h1>
Login and Registration!
</h1>
<p>
This is a placeholder for the combined login and registration form
</p>
## TODO: Use JavaScript to populate this div with
## the actual registration/login forms (loaded asynchronously from the user API)
## The URLS for the forms are:
## - GET /user_api/v1/registration/
## - GET /user_api/v1/login_session/
##
## You can post back to those URLs with JSON-serialized
## data from the form fields in order to complete the registration
## or login.
##
## Also TODO: we need to figure out how to enroll students in
## a course if they got here from a course about page.
##
## third_party_auth_providers is a JSON-serialized list of
## dictionaries of the form:
## {
## "name": "Facebook",
## "icon_class": "facebook-icon",
## "login_url": "http://api.facebook.com/auth"
## }
##
## Note that this list may be empty.
##
<div
id=
"login-and-registration-container"
data-initial-mode=
"${initial_mode}"
data-third-party-auth-providers=
"${third_party_auth_providers}"
/>
lms/urls.py
View file @
e89afa93
...
...
@@ -375,6 +375,10 @@ if settings.COURSEWARE_ENABLED:
# LTI endpoints listing
url
(
r'^courses/{}/lti_rest_endpoints/'
.
format
(
settings
.
COURSE_ID_PATTERN
),
'courseware.views.get_course_lti_endpoints'
,
name
=
'lti_rest_endpoints'
),
# Student account and profile
url
(
r'^account/'
,
include
(
'student_account.urls'
)),
url
(
r'^profile/'
,
include
(
'student_profile.urls'
)),
)
# allow course staff to change to student view of courseware
...
...
@@ -537,12 +541,6 @@ if settings.FEATURES.get('ENABLE_THIRD_PARTY_AUTH'):
url
(
r''
,
include
(
'third_party_auth.urls'
)),
)
# If enabled, expose the URLs for the new dashboard, account, and profile pages
if
settings
.
FEATURES
.
get
(
'ENABLE_NEW_DASHBOARD'
):
urlpatterns
+=
(
url
(
r'^profile/'
,
include
(
'student_profile.urls'
)),
url
(
r'^account/'
,
include
(
'student_account.urls'
)),
)
urlpatterns
=
patterns
(
*
urlpatterns
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment