Halfway state for course enrollment by mode
This commit is contained in:
@@ -51,3 +51,8 @@ class CourseMode(models.Model):
|
||||
if not modes:
|
||||
modes = [cls.DEFAULT_MODE]
|
||||
return modes
|
||||
|
||||
def __unicode__(self):
|
||||
return u"{} : {}, min={}, prices={}".format(
|
||||
self.course_id, self.mode_slug, self.min_price, self.suggested_prices
|
||||
)
|
||||
|
||||
@@ -1 +1,27 @@
|
||||
# Create your views here.
|
||||
from django.http import HttpResponse
|
||||
from django.views.generic.base import View
|
||||
|
||||
from mitxmako.shortcuts import render_to_response
|
||||
|
||||
from course_modes.models import CourseMode
|
||||
|
||||
class ChooseModeView(View):
|
||||
|
||||
def get(self, request):
|
||||
course_id = request.GET.get("course_id")
|
||||
context = {
|
||||
"course_id" : course_id,
|
||||
"available_modes" : CourseMode.modes_for_course(course_id)
|
||||
}
|
||||
return render_to_response("course_modes/choose.html", context)
|
||||
|
||||
def post(self, request):
|
||||
course_id = request.GET.get("course_id")
|
||||
mode_slug = request.POST.get("mode_slug")
|
||||
user = request.user
|
||||
|
||||
# This is a bit redundant with logic in student.views.change_enrollement,
|
||||
# but I don't really have the time to refactor it more nicely and test.
|
||||
course = course_from_id(course_id)
|
||||
if has_access(user, course, 'enroll'):
|
||||
pass
|
||||
|
||||
@@ -27,18 +27,19 @@ from django_future.csrf import ensure_csrf_cookie
|
||||
from django.utils.http import cookie_date
|
||||
from django.utils.http import base36_to_int
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.views.decorators.http import require_POST
|
||||
|
||||
from ratelimitbackend.exceptions import RateLimitException
|
||||
|
||||
from mitxmako.shortcuts import render_to_response, render_to_string
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
from course_modes.models import CourseMode
|
||||
from student.models import (Registration, UserProfile, TestCenterUser, TestCenterUserForm,
|
||||
TestCenterRegistration, TestCenterRegistrationForm,
|
||||
PendingNameChange, PendingEmailChange,
|
||||
CourseEnrollment, unique_id_for_user,
|
||||
get_testcenter_registration, CourseEnrollmentAllowed)
|
||||
|
||||
from student.forms import PasswordResetFormNoActive
|
||||
|
||||
from certificates.models import CertificateStatuses, certificate_status_for_student
|
||||
@@ -328,7 +329,8 @@ def try_change_enrollment(request):
|
||||
except Exception, e:
|
||||
log.exception("Exception automatically enrolling after login: {0}".format(str(e)))
|
||||
|
||||
|
||||
@login_required
|
||||
@require_POST
|
||||
def change_enrollment(request):
|
||||
"""
|
||||
Modify the enrollment status for the logged-in user.
|
||||
@@ -346,12 +348,7 @@ def change_enrollment(request):
|
||||
as a post-login/registration helper, so the error messages in the responses
|
||||
should never actually be user-visible.
|
||||
"""
|
||||
if request.method != "POST":
|
||||
return HttpResponseNotAllowed(["POST"])
|
||||
|
||||
user = request.user
|
||||
if not user.is_authenticated():
|
||||
return HttpResponseForbidden()
|
||||
|
||||
action = request.POST.get("enrollment_action")
|
||||
course_id = request.POST.get("course_id")
|
||||
@@ -371,6 +368,12 @@ def change_enrollment(request):
|
||||
if not has_access(user, course, 'enroll'):
|
||||
return HttpResponseBadRequest(_("Enrollment is closed"))
|
||||
|
||||
# If this course is available in multiple modes, redirect them to a page
|
||||
# where they can choose which mode they want.
|
||||
available_modes = CourseMode.modes_for_course(course_id)
|
||||
if len(available_modes) > 1:
|
||||
return HttpResponse(reverse("course_modes.views.choose"))
|
||||
|
||||
org, course_num, run = course_id.split("/")
|
||||
statsd.increment("common.student.enrollment",
|
||||
tags=["org:{0}".format(org),
|
||||
|
||||
@@ -7,7 +7,7 @@ from student.tests.factories import UserFactory
|
||||
from verify_student.models import SoftwareSecurePhotoVerification, VerificationException
|
||||
|
||||
|
||||
class TestPhotoVerification(object):
|
||||
class TestPhotoVerification(TestCase):
|
||||
|
||||
def test_state_transitions(self):
|
||||
"""Make sure we can't make unexpected status transitions.
|
||||
|
||||
@@ -4,7 +4,7 @@ from nose.tools import assert_equals
|
||||
|
||||
from verify_student.ssencrypt import (
|
||||
aes_decrypt, aes_encrypt, encrypt_and_encode, decode_and_decrypt,
|
||||
rsa_decrypt, rsa_encrypt
|
||||
rsa_decrypt, rsa_encrypt, random_aes_key
|
||||
)
|
||||
|
||||
def test_aes():
|
||||
@@ -76,8 +76,3 @@ l8N6+LEIVTMAytPk+/bImHvGHKZkCz5rEMSuYJWOmqKI92rUtI6fz5DUb3XSbrwT
|
||||
# Even though our AES key is only 32 bytes, RSA encryption will make it 256
|
||||
# bytes, and base64 encoding will blow that up to 344
|
||||
assert_equals(len(base64.urlsafe_b64encode(encrypted_aes_key)), 344)
|
||||
|
||||
# Software Secure would decrypt our photo_id image by doing:
|
||||
#rsa_encrypted_aes_key = base64.urlsafe_b64decode(encoded_photo_id_key)
|
||||
#photo_id_aes_key = rsa_decrypt(rsa_encrypted_aes_key, priv_key_str)
|
||||
|
||||
|
||||
@@ -31,7 +31,8 @@ class StartView(TestCase):
|
||||
user = UserFactory.create(username="rusty", password="test")
|
||||
self.client.login(username="rusty", password="test")
|
||||
|
||||
|
||||
|
||||
def must_be_logged_in(self):
|
||||
self.assertHttpForbidden(self.client.get(self.start_url()))
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ from mitxmako.shortcuts import render_to_response
|
||||
|
||||
from verify_student.models import SoftwareSecurePhotoVerification
|
||||
|
||||
from course_modes.models import CourseMode
|
||||
|
||||
# @login_required
|
||||
def start_or_resume_attempt(request, course_id):
|
||||
"""
|
||||
@@ -15,8 +17,8 @@ def start_or_resume_attempt(request, course_id):
|
||||
"""
|
||||
# If the user has already been verified within the given time period,
|
||||
# redirect straight to the payment -- no need to verify again.
|
||||
#if SoftwareSecurePhotoVerification.user_is_verified(user):
|
||||
# pass
|
||||
if SoftwareSecurePhotoVerification.user_is_verified(user):
|
||||
pass
|
||||
|
||||
attempt = SoftwareSecurePhotoVerification.active_for_user(request.user)
|
||||
if not attempt:
|
||||
@@ -47,3 +49,62 @@ def final_verification(request):
|
||||
def show_verification_page(request):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
def enroll(user, course_id, mode_slug):
|
||||
"""
|
||||
Enroll the user in a course for a certain mode.
|
||||
|
||||
This is the view you send folks to when they click on the enroll button.
|
||||
This does NOT cover changing enrollment modes -- it's intended for new
|
||||
enrollments only, and will just redirect to the dashboard if it detects
|
||||
that an enrollment already exists.
|
||||
"""
|
||||
# If the user is already enrolled, jump to the dashboard. Yeah, we could
|
||||
# do upgrades here, but this method is complicated enough.
|
||||
if CourseEnrollment.is_enrolled(user, course_id):
|
||||
return HttpResponseRedirect(reverse('dashboard'))
|
||||
|
||||
available_modes = CourseModes.modes_for_course(course_id)
|
||||
|
||||
# If they haven't chosen a mode...
|
||||
if not mode_slug:
|
||||
# Does this course support multiple modes of Enrollment? If so, redirect
|
||||
# to a page that lets them choose which mode they want.
|
||||
if len(available_modes) > 1:
|
||||
return HttpResponseRedirect(
|
||||
reverse('choose_enroll_mode', course_id=course_id)
|
||||
)
|
||||
# Otherwise, we use the only mode that's supported...
|
||||
else:
|
||||
mode_slug = available_modes[0].slug
|
||||
|
||||
# If the mode is one of the simple, non-payment ones, do the enrollment and
|
||||
# send them to their dashboard.
|
||||
if mode_slug in ("honor", "audit"):
|
||||
CourseEnrollment.enroll(user, course_id, mode=mode_slug)
|
||||
return HttpResponseRedirect(reverse('dashboard'))
|
||||
|
||||
if mode_slug == "verify":
|
||||
if SoftwareSecureVerification.has_submitted_recent_request(user):
|
||||
# Capture payment info
|
||||
# Create an order
|
||||
# Create a VerifiedCertificate order item
|
||||
return HttpResponse.Redirect(reverse('payment'))
|
||||
|
||||
|
||||
# There's always at least one mode available (default is "honor"). If they
|
||||
# haven't specified a mode, we just assume it's
|
||||
if not mode:
|
||||
mode = available_modes[0]
|
||||
|
||||
elif len(available_modes) == 1:
|
||||
if mode != available_modes[0]:
|
||||
raise Exception()
|
||||
|
||||
mode = available_modes[0]
|
||||
|
||||
if mode == "honor":
|
||||
CourseEnrollment.enroll(user, course_id)
|
||||
return HttpResponseRedirect(reverse('dashboard'))
|
||||
|
||||
|
||||
@@ -47,7 +47,12 @@
|
||||
|
||||
$('#class_enroll_form').on('ajax:complete', function(event, xhr) {
|
||||
if(xhr.status == 200) {
|
||||
location.href = "${reverse('dashboard')}";
|
||||
if (xhr.responseText == "") {
|
||||
location.href = "${reverse('dashboard')}";
|
||||
}
|
||||
else {
|
||||
location.href = xhr.responseText;
|
||||
}
|
||||
} else if (xhr.status == 403) {
|
||||
location.href = "${reverse('register_user')}?course_id=${course.id}&enrollment_action=enroll";
|
||||
} else {
|
||||
|
||||
@@ -65,7 +65,10 @@ urlpatterns = ('', # nopep8
|
||||
)
|
||||
|
||||
if settings.MITX_FEATURES.get("MULTIPLE_ENROLLMENT_ROLES"):
|
||||
urlpatterns += (url(r'^verify_student/', include('verify_student.urls')),)
|
||||
urlpatterns += (
|
||||
url(r'^verify_student/', include('verify_student.urls')),
|
||||
url(r'^course_modes/', include('course_modes.urls')),
|
||||
)
|
||||
|
||||
|
||||
js_info_dict = {
|
||||
|
||||
Reference in New Issue
Block a user