Commit 8d7d9d21 by Diana Huang

Merge pull request #5762 from edx/diana/enrollment-integration

WIP: Combined Login/Registration: Set up basic enrollment infrastructure.
parents 471f5135 47979e5b
...@@ -31,6 +31,7 @@ class CourseModeViewTest(ModuleStoreTestCase): ...@@ -31,6 +31,7 @@ class CourseModeViewTest(ModuleStoreTestCase):
self.user = UserFactory.create(username="Bob", email="bob@example.com", password="edx") self.user = UserFactory.create(username="Bob", email="bob@example.com", password="edx")
self.client.login(username=self.user.username, password="edx") self.client.login(username=self.user.username, password="edx")
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
@ddt.data( @ddt.data(
# is_active?, enrollment_mode, upgrade?, redirect? # is_active?, enrollment_mode, upgrade?, redirect?
(True, 'verified', True, False), # User has an active verified enrollment and is trying to upgrade (True, 'verified', True, False), # User has an active verified enrollment and is trying to upgrade
......
...@@ -26,10 +26,10 @@ class ChooseModeView(View): ...@@ -26,10 +26,10 @@ class ChooseModeView(View):
When a get request is used, shows the selection page. When a get request is used, shows the selection page.
When a post request is used, assumes that it is a form submission When a post request is used, assumes that it is a form submission
from the selection page, parses the response, and then sends user from the selection page, parses the response, and then sends user
to the next step in the flow. to the next step in the flow.
""" """
@method_decorator(login_required) @method_decorator(login_required)
...@@ -50,7 +50,7 @@ class ChooseModeView(View): ...@@ -50,7 +50,7 @@ class ChooseModeView(View):
""" """
course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
enrollment_mode, is_active = CourseEnrollment.enrollment_mode_for_user(request.user, course_key) enrollment_mode, is_active = CourseEnrollment.enrollment_mode_for_user(request.user, course_key)
upgrade = request.GET.get('upgrade', False) upgrade = request.GET.get('upgrade', False)
request.session['attempting_upgrade'] = upgrade request.session['attempting_upgrade'] = upgrade
......
...@@ -73,7 +73,7 @@ def update_course_enrollment(student_id, course_id, mode=None, is_active=None): ...@@ -73,7 +73,7 @@ def update_course_enrollment(student_id, course_id, mode=None, is_active=None):
course_key = CourseKey.from_string(course_id) course_key = CourseKey.from_string(course_id)
student = User.objects.get(username=student_id) student = User.objects.get(username=student_id)
if not CourseEnrollment.is_enrolled(student, course_key): if not CourseEnrollment.is_enrolled(student, course_key):
enrollment = CourseEnrollment.enroll(student, course_key) enrollment = CourseEnrollment.enroll(student, course_key, check_access=True)
else: else:
enrollment = CourseEnrollment.objects.get(user=student, course_id=course_key) enrollment = CourseEnrollment.objects.get(user=student, course_id=course_key)
......
...@@ -10,7 +10,7 @@ from rest_framework.permissions import IsAuthenticated ...@@ -10,7 +10,7 @@ from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.throttling import UserRateThrottle from rest_framework.throttling import UserRateThrottle
from enrollment import api from enrollment import api
from student.models import NonExistentCourseError from student.models import NonExistentCourseError, CourseEnrollmentException
class EnrollmentUserThrottle(UserRateThrottle): class EnrollmentUserThrottle(UserRateThrottle):
...@@ -74,3 +74,5 @@ def get_course_enrollment(request, course_id=None): ...@@ -74,3 +74,5 @@ def get_course_enrollment(request, course_id=None):
return Response(status=status.HTTP_400_BAD_REQUEST) return Response(status=status.HTTP_400_BAD_REQUEST)
except api.EnrollmentNotFoundError: except api.EnrollmentNotFoundError:
return Response(status=status.HTTP_400_BAD_REQUEST) return Response(status=status.HTTP_400_BAD_REQUEST)
except CourseEnrollmentException:
return Response(status=status.HTTP_400_BAD_REQUEST)
...@@ -1028,6 +1028,7 @@ instructor_dash_js = sorted(rooted_glob(PROJECT_ROOT / 'static', 'coffee/src/ins ...@@ -1028,6 +1028,7 @@ instructor_dash_js = sorted(rooted_glob(PROJECT_ROOT / 'static', 'coffee/src/ins
student_account_js = [ student_account_js = [
'js/common_helpers/rwd_header_footer.js', 'js/common_helpers/rwd_header_footer.js',
'js/common_helpers/edx.utils.validate.js', 'js/common_helpers/edx.utils.validate.js',
'js/student_account/enrollment_interface.js',
'js/student_account/models/LoginModel.js', 'js/student_account/models/LoginModel.js',
'js/student_account/models/RegisterModel.js', 'js/student_account/models/RegisterModel.js',
'js/student_account/models/PasswordResetModel.js', 'js/student_account/models/PasswordResetModel.js',
......
...@@ -260,7 +260,10 @@ ...@@ -260,7 +260,10 @@
exports: 'NotificationView', exports: 'NotificationView',
deps: ['backbone', 'jquery', 'underscore'] deps: ['backbone', 'jquery', 'underscore']
}, },
'js/student_account/enrollment_interface': {
exports: 'js/student_account/enrollment_interface',
deps: ['jquery', 'underscore', 'gettext']
},
// Student account registration/login // Student account registration/login
// Loaded explicitly until these are converted to RequireJS // Loaded explicitly until these are converted to RequireJS
'js/student_account/views/FormView': { 'js/student_account/views/FormView': {
...@@ -310,7 +313,7 @@ ...@@ -310,7 +313,7 @@
'js/student_account/views/RegisterView', 'js/student_account/views/RegisterView',
'underscore.string' 'underscore.string'
] ]
}, }
}, },
}); });
...@@ -327,6 +330,7 @@ ...@@ -327,6 +330,7 @@
'lms/include/js/spec/student_account/login_spec.js', 'lms/include/js/spec/student_account/login_spec.js',
'lms/include/js/spec/student_account/register_spec.js', 'lms/include/js/spec/student_account/register_spec.js',
'lms/include/js/spec/student_account/password_reset_spec.js', 'lms/include/js/spec/student_account/password_reset_spec.js',
'lms/include/js/spec/student_account/enrollment_interface_spec.js',
'lms/include/js/spec/student_profile/profile.js', 'lms/include/js/spec/student_profile/profile.js',
]); ]);
......
define(['js/common_helpers/template_helpers', 'js/student_account/views/AccessView'], define(['js/common_helpers/template_helpers', 'js/student_account/views/AccessView'],
function(TemplateHelpers) { function(TemplateHelpers, AccessView) {
describe('edx.student.account.AccessView', function() { describe('edx.student.account.AccessView', function() {
'use strict'; 'use strict';
......
define(['js/common_helpers/template_helpers', 'js/student_account/enrollment_interface'],
function(TemplateHelpers, EnrollmentInterface) {
describe("edx.student.account.EnrollmentInterface", function() {
'use strict';
it("find course modes using modeInArray ", function() {
var course_modes = [
{
slug: 'honor'
},
{
slug: 'professional'
}
],
expect(EnrollmentInterface.modeInArray('professional')).toBe(true);
expect(EnrollmentInterface.modeInArray('audit')).toBe(false);
});
});
}
);
...@@ -39,6 +39,10 @@ define(['js/common_helpers/template_helpers', 'js/student_account/views/LoginVie ...@@ -39,6 +39,10 @@ define(['js/common_helpers/template_helpers', 'js/student_account/views/LoginVie
it("allows the user to navigate to the password assistance form", function() { it("allows the user to navigate to the password assistance form", function() {
// TODO // TODO
}); });
it("enrolls the student into the right location and forwards them properly", function() {
// TODO
});
}); });
} }
); );
var edx = edx || {};
(function($, _, gettext) {
'use strict';
edx.student = edx.student || {};
edx.student.account = edx.student.account || {};
edx.student.account.EnrollmentInterface = {
courseUrl: '/enrollment/v0/course/',
studentUrl: '/enrollment/v0/student',
trackSelectionUrl: '/course_modes/choose/',
headers: {
'X-CSRFToken': $.cookie('csrftoken')
},
studentInformation: function(course_key) {
// retrieve student enrollment information
},
courseInformation: function(course_key) {
// retrieve course information from the enrollment API
},
modeInArray: function(mode_slug, course_modes) {
// finds whether or not a particular course mode slug exists
// in an array of course modes
var result = _.find(course_modes, function(mode){ return mode.slug === mode_slug; });
return result != undefined;
},
enroll: function(course_key, forward_url){
var me = this;
// attempt to enroll a student in a course
$.ajax({
url: this.courseUrl + course_key,
type: 'POST',
data: {},
headers: this.headers
}).done(function(data){
me.postEnrollmentHandler(course_key, data, forward_url);
}
).fail(function(data, textStatus) {
me.enrollmentFailureHandler(course_key, data, forward_url);
});
},
enrollmentFailureHandler: function(course_key, data, forward_url) {
// handle failures to enroll via the API
if(data.status == 400) {
// This status code probably means we don't have permissions to register for this course.
// look at the contents of the response
var course = $.parseJSON(data.responseText);
// see if it's a professional ed course
if('course_modes' in course && this.modeInArray('professional', course.course_modes)) {
// forward appropriately
forward_url = this.trackSelectionUrl + course_key;
}
}
// TODO: if we have a paid registration mode, add item to the cart and send them along
// TODO: we should figure out how to handle errors here eventually
window.location.href = forward_url;
},
postEnrollmentHandler: function(course_key, data, forward_url) {
// Determine whether or not the course needs to be redirected to
// a particular page.
var course = data.course,
course_modes = course.course_modes;
// send the user to the track selection page, because it will do the right thing
forward_url = this.trackSelectionUrl + course_key;
window.location.href = forward_url;
}
};
})(jQuery, _, gettext);
...@@ -6,6 +6,7 @@ var edx = edx || {}; ...@@ -6,6 +6,7 @@ var edx = edx || {};
edx.student = edx.student || {}; edx.student = edx.student || {};
edx.student.account = edx.student.account || {}; edx.student.account = edx.student.account || {};
edx.student.account.LoginModel = Backbone.Model.extend({ edx.student.account.LoginModel = Backbone.Model.extend({
defaults: { defaults: {
...@@ -32,17 +33,31 @@ var edx = edx || {}; ...@@ -32,17 +33,31 @@ var edx = edx || {};
headers: headers headers: headers
}) })
.done(function() { .done(function() {
var query = window.location.search, var enrollment = edx.student.account.EnrollmentInterface,
url = '/dashboard'; query = new URI(window.location.search),
url = '/dashboard',
query_map = query.search(true),
next = '';
// check for forwarding url
if("next" in query_map) {
next = query_map['next'];
if(!window.isExternal(next)){
url = next;
}
}
model.trigger('sync'); model.trigger('sync');
// If query string in url go back to that page // if we need to enroll in the course, mark as enrolled
if ( query.length > 1 ) { if('enrollment_action' in query_map && query_map['enrollment_action'] === 'enroll'){
url = query.substring( query.indexOf('=') + 1 ); enrollment.enroll(query_map['course_id'], url);
} }
else {
window.location.href = url;
}
window.location.href = url;
}) })
.fail( function( error ) { .fail( function( error ) {
model.trigger('error', error); model.trigger('error', error);
......
...@@ -39,17 +39,29 @@ var edx = edx || {}; ...@@ -39,17 +39,29 @@ var edx = edx || {};
headers: headers headers: headers
}) })
.done(function() { .done(function() {
var query = window.location.search, var enrollment = edx.student.account.EnrollmentInterface,
url = '/dashboard'; query = new URI(window.location.search),
url = '/dashboard',
query_map = query.search(true),
next = '';
// check for forwarding url
if("next" in query_map) {
next = query_map['next'];
if(!window.isExternal(next)){
url = next;
}
}
model.trigger('sync'); model.trigger('sync');
// If query string in url go back to that page // if we need to enroll in the course, mark as enrolled
if ( query.length > 1 ) { if('enrollment_action' in query_map && query_map['enrollment_action'] === 'enroll'){
url = query.substring( query.indexOf('=') + 1 ); enrollment.enroll(query_map['course_id'], url);
}
else {
window.location.href = url;
} }
window.location.href = url;
}) })
.fail( function( error ) { .fail( function( error ) {
model.trigger('error', error); model.trigger('error', error);
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
<%block name="js_extra"> <%block name="js_extra">
<script type="text/javascript" src="${static.url('js/vendor/backbone-min.js')}"></script> <script type="text/javascript" src="${static.url('js/vendor/backbone-min.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/underscore.string.min.js')}"></script> <script type="text/javascript" src="${static.url('js/vendor/underscore.string.min.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/URI.min.js')}"></script>
<%static:js group='student_account'/> <%static:js group='student_account'/>
</%block> </%block>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment