Commit 2312b222 by Vedran Karačić

Merge pull request #11503 from edx/coupons/otto-checkout

Checkout on Otto
parents eb914a00 86a4710e
...@@ -3,28 +3,27 @@ Views for the course_mode module ...@@ -3,28 +3,27 @@ Views for the course_mode module
""" """
import decimal import decimal
from ipware.ip import get_ip
from django.contrib.auth.decorators import login_required
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db import transaction from django.db import transaction
from django.http import HttpResponse, HttpResponseBadRequest from django.http import HttpResponse, HttpResponseBadRequest
from django.shortcuts import redirect from django.shortcuts import redirect
from django.views.generic.base import View
from django.utils.translation import ugettext as _
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.utils.translation import ugettext as _
from django.views.generic.base import View
from ipware.ip import get_ip
from opaque_keys.edx.keys import CourseKey
from opaque_keys.edx.locations import SlashSeparatedCourseKey
from xmodule.modulestore.django import modulestore
from edxmako.shortcuts import render_to_response from lms.djangoapps.commerce.utils import EcommerceService
from course_modes.models import CourseMode from course_modes.models import CourseMode
from courseware.access import has_access from courseware.access import has_access
from edxmako.shortcuts import render_to_response
from embargo import api as embargo_api
from student.models import CourseEnrollment from student.models import CourseEnrollment
from opaque_keys.edx.locations import SlashSeparatedCourseKey
from opaque_keys.edx.keys import CourseKey
from util.db import outer_atomic from util.db import outer_atomic
from xmodule.modulestore.django import modulestore
from embargo import api as embargo_api
class ChooseModeView(View): class ChooseModeView(View):
...@@ -39,7 +38,14 @@ class ChooseModeView(View): ...@@ -39,7 +38,14 @@ class ChooseModeView(View):
""" """
@method_decorator(transaction.non_atomic_requests) @method_decorator(transaction.non_atomic_requests)
def dispatch(self, *args, **kwargs): # pylint: disable=missing-docstring def dispatch(self, *args, **kwargs):
"""Disable atomicity for the view.
Otherwise, we'd be unable to commit to the database until the
request had concluded; Django will refuse to commit when an
atomic() block is active, since that would break atomicity.
"""
return super(ChooseModeView, self).dispatch(*args, **kwargs) return super(ChooseModeView, self).dispatch(*args, **kwargs)
@method_decorator(login_required) @method_decorator(login_required)
...@@ -117,7 +123,10 @@ class ChooseModeView(View): ...@@ -117,7 +123,10 @@ class ChooseModeView(View):
) )
context = { context = {
"course_modes_choose_url": reverse("course_modes_choose", kwargs={'course_id': course_key.to_deprecated_string()}), "course_modes_choose_url": reverse(
"course_modes_choose",
kwargs={'course_id': course_key.to_deprecated_string()}
),
"modes": modes, "modes": modes,
"has_credit_upsell": has_credit_upsell, "has_credit_upsell": has_credit_upsell,
"course_name": course.display_name_with_default_escaped, "course_name": course.display_name_with_default_escaped,
...@@ -129,15 +138,22 @@ class ChooseModeView(View): ...@@ -129,15 +138,22 @@ class ChooseModeView(View):
"nav_hidden": True, "nav_hidden": True,
} }
if "verified" in modes: if "verified" in modes:
verified_mode = modes["verified"]
context["suggested_prices"] = [ context["suggested_prices"] = [
decimal.Decimal(x.strip()) decimal.Decimal(x.strip())
for x in modes["verified"].suggested_prices.split(",") for x in verified_mode.suggested_prices.split(",")
if x.strip() if x.strip()
] ]
context["currency"] = modes["verified"].currency.upper() context["currency"] = verified_mode.currency.upper()
context["min_price"] = modes["verified"].min_price context["min_price"] = verified_mode.min_price
context["verified_name"] = modes["verified"].name context["verified_name"] = verified_mode.name
context["verified_description"] = modes["verified"].description context["verified_description"] = verified_mode.description
if verified_mode.sku:
ecommerce_service = EcommerceService()
context["use_ecommerce_payment_flow"] = ecommerce_service.is_enabled()
context["ecommerce_payment_page"] = ecommerce_service.payment_page_url()
context["sku"] = verified_mode.sku
return render_to_response("course_modes/choose.html", context) return render_to_response("course_modes/choose.html", context)
......
...@@ -207,7 +207,7 @@ def get_next_url_for_login_page(request): ...@@ -207,7 +207,7 @@ def get_next_url_for_login_page(request):
""" """
Determine the URL to redirect to following login/registration/third_party_auth Determine the URL to redirect to following login/registration/third_party_auth
The user is currently on a login or reigration page. The user is currently on a login or registration page.
If 'course_id' is set, or other POST_AUTH_PARAMS, we will need to send the user to the If 'course_id' is set, or other POST_AUTH_PARAMS, we will need to send the user to the
/account/finish_auth/ view following login, which will take care of auto-enrollment in /account/finish_auth/ view following login, which will take care of auto-enrollment in
the specified course. the specified course.
......
...@@ -39,7 +39,6 @@ from django.template.response import TemplateResponse ...@@ -39,7 +39,6 @@ from django.template.response import TemplateResponse
from ratelimitbackend.exceptions import RateLimitException from ratelimitbackend.exceptions import RateLimitException
from social.apps.django_app import utils as social_utils from social.apps.django_app import utils as social_utils
from social.backends import oauth as social_oauth from social.backends import oauth as social_oauth
from social.exceptions import AuthException, AuthAlreadyAssociated from social.exceptions import AuthException, AuthAlreadyAssociated
...@@ -55,6 +54,7 @@ from student.models import ( ...@@ -55,6 +54,7 @@ from student.models import (
create_comments_service_user, PasswordHistory, UserSignupSource, create_comments_service_user, PasswordHistory, UserSignupSource,
DashboardConfiguration, LinkedInAddToProfileConfiguration, ManualEnrollmentAudit, ALLOWEDTOENROLL_TO_ENROLLED) DashboardConfiguration, LinkedInAddToProfileConfiguration, ManualEnrollmentAudit, ALLOWEDTOENROLL_TO_ENROLLED)
from student.forms import AccountCreationForm, PasswordResetFormNoActive, get_registration_extension_form from student.forms import AccountCreationForm, PasswordResetFormNoActive, get_registration_extension_form
from lms.djangoapps.commerce.utils import EcommerceService # pylint: disable=import-error
from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification # pylint: disable=import-error from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification # pylint: disable=import-error
from certificates.models import CertificateStatuses, certificate_status_for_student from certificates.models import CertificateStatuses, certificate_status_for_student
from certificates.api import ( # pylint: disable=import-error from certificates.api import ( # pylint: disable=import-error
...@@ -502,6 +502,7 @@ def complete_course_mode_info(course_id, enrollment, modes=None): ...@@ -502,6 +502,7 @@ def complete_course_mode_info(course_id, enrollment, modes=None):
# if verified is an option. # if verified is an option.
if CourseMode.VERIFIED in modes and enrollment.mode in CourseMode.UPSELL_TO_VERIFIED_MODES: if CourseMode.VERIFIED in modes and enrollment.mode in CourseMode.UPSELL_TO_VERIFIED_MODES:
mode_info['show_upsell'] = True mode_info['show_upsell'] = True
mode_info['verified_sku'] = modes['verified'].sku
# if there is an expiration date, find out how long from now it is # if there is an expiration date, find out how long from now it is
if modes['verified'].expiration_datetime: if modes['verified'].expiration_datetime:
today = datetime.datetime.now(UTC).date() today = datetime.datetime.now(UTC).date()
...@@ -737,6 +738,13 @@ def dashboard(request): ...@@ -737,6 +738,13 @@ def dashboard(request):
'xseries_credentials': xseries_credentials, 'xseries_credentials': xseries_credentials,
} }
ecommerce_service = EcommerceService()
if ecommerce_service.is_enabled():
context.update({
'use_ecommerce_payment_flow': True,
'ecommerce_payment_page': ecommerce_service.payment_page_url(),
})
return render_to_response('dashboard.html', context) return render_to_response('dashboard.html', context)
......
""" Admin site bindings for commerce app. """
from django.contrib import admin
from commerce.models import CommerceConfiguration
from config_models.admin import ConfigurationModelAdmin
admin.site.register(CommerceConfiguration, ConfigurationModelAdmin)
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('commerce', '0001_data__add_ecommerce_service_user'),
]
operations = [
migrations.CreateModel(
name='CommerceConfiguration',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('change_date', models.DateTimeField(auto_now_add=True, verbose_name='Change date')),
('enabled', models.BooleanField(default=False, verbose_name='Enabled')),
('checkout_on_ecommerce_service', models.BooleanField(default=False, help_text='Use the checkout page hosted by the E-Commerce service.')),
('single_course_checkout_page', models.CharField(default=b'/basket/single-item/', help_text='Path to single course checkout page hosted by the E-Commerce service.', max_length=255)),
('changed_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, editable=False, to=settings.AUTH_USER_MODEL, null=True, verbose_name='Changed by')),
],
options={
'ordering': ('-change_date',),
'abstract': False,
},
),
]
""" """
This file is intentionally empty. Django 1.6 and below require a models.py file for all apps. Commerce-related models.
""" """
from django.db import models
from django.utils.translation import ugettext_lazy as _
from config_models.models import ConfigurationModel
class CommerceConfiguration(ConfigurationModel):
""" Commerce configuration """
checkout_on_ecommerce_service = models.BooleanField(
default=False,
help_text=_('Use the checkout page hosted by the E-Commerce service.')
)
single_course_checkout_page = models.CharField(
max_length=255,
default='/basket/single-item/',
help_text=_('Path to single course checkout page hosted by the E-Commerce service.')
)
def __unicode__(self):
return "Commerce configuration"
"""Tests of commerce utilities.""" """Tests of commerce utilities."""
from django.test import TestCase from django.test import TestCase
from django.test.utils import override_settings
from mock import patch from mock import patch
from commerce.utils import audit_log from commerce.utils import audit_log, EcommerceService
from commerce.models import CommerceConfiguration
class AuditLogTests(TestCase): class AuditLogTests(TestCase):
...@@ -16,3 +18,46 @@ class AuditLogTests(TestCase): ...@@ -16,3 +18,46 @@ class AuditLogTests(TestCase):
# key-value pairs ordered alphabetically by key. # key-value pairs ordered alphabetically by key.
message = 'foo: bar="baz", qux="quux"' message = 'foo: bar="baz", qux="quux"'
self.assertTrue(mock_log.info.called_with(message)) self.assertTrue(mock_log.info.called_with(message))
class EcommerceServiceTests(TestCase):
"""Tests for the EcommerceService helper class."""
SKU = 'TESTSKU'
def setUp(self):
CommerceConfiguration.objects.create(
checkout_on_ecommerce_service=True,
single_course_checkout_page='/test_basket/'
)
super(EcommerceServiceTests, self).setUp()
def test_is_enabled(self):
"""Verify that is_enabled() returns True when ecomm checkout is enabled. """
is_enabled = EcommerceService().is_enabled()
self.assertTrue(is_enabled)
config = CommerceConfiguration.current()
config.checkout_on_ecommerce_service = False
config.save()
is_not_enabled = EcommerceService().is_enabled()
self.assertFalse(is_not_enabled)
@patch('openedx.core.djangoapps.theming.helpers.is_request_in_themed_site')
def test_is_enabled_for_microsites(self, is_microsite):
"""Verify that is_enabled() returns False if used for a microsite."""
is_microsite.return_value = True
is_not_enabled = EcommerceService().is_enabled()
self.assertFalse(is_not_enabled)
@override_settings(ECOMMERCE_PUBLIC_URL_ROOT='http://ecommerce_url')
def test_payment_page_url(self):
"""Verify that the proper URL is returned."""
url = EcommerceService().payment_page_url()
self.assertEqual(url, 'http://ecommerce_url/test_basket/')
@override_settings(ECOMMERCE_PUBLIC_URL_ROOT='http://ecommerce_url')
def test_checkout_page_url(self):
""" Verify the checkout page URL is properly constructed and returned. """
url = EcommerceService().checkout_page_url(self.SKU)
expected_url = 'http://ecommerce_url/test_basket/?sku={}'.format(self.SKU)
self.assertEqual(url, expected_url)
"""Utilities to assist with commerce tasks.""" """Utilities to assist with commerce tasks."""
import logging import logging
from urlparse import urljoin
from django.conf import settings
from commerce.models import CommerceConfiguration
from openedx.core.djangoapps.theming import helpers
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -32,3 +37,29 @@ def audit_log(name, **kwargs): ...@@ -32,3 +37,29 @@ def audit_log(name, **kwargs):
message = u'{name}: {payload}'.format(name=name, payload=payload) message = u'{name}: {payload}'.format(name=name, payload=payload)
log.info(message) log.info(message)
class EcommerceService(object):
""" Helper class for ecommerce service integration. """
def __init__(self):
self.config = CommerceConfiguration.current()
def is_enabled(self):
""" Check if the service is enabled and that the site is not a microsite. """
return self.config.checkout_on_ecommerce_service and not helpers.is_request_in_themed_site()
def payment_page_url(self):
""" Return the URL for the checkout page.
Example:
http://localhost:8002/basket/single_item/
"""
return urljoin(settings.ECOMMERCE_PUBLIC_URL_ROOT, self.config.single_course_checkout_page)
def checkout_page_url(self, sku):
""" Construct the URL to the ecommerce checkout page and include a product.
Example:
http://localhost:8002/basket/single_item/?sku=5H3HG5
"""
return "{}?sku={}".format(self.payment_page_url(), sku)
...@@ -39,6 +39,7 @@ import survey.utils ...@@ -39,6 +39,7 @@ import survey.utils
import survey.views import survey.views
from certificates import api as certs_api from certificates import api as certs_api
from openedx.core.lib.gating import api as gating_api from openedx.core.lib.gating import api as gating_api
from commerce.utils import EcommerceService
from course_modes.models import CourseMode from course_modes.models import CourseMode
from courseware import grades from courseware import grades
from courseware.access import has_access, has_ccx_coach_role, _adjust_start_date_for_beta_testers from courseware.access import has_access, has_ccx_coach_role, _adjust_start_date_for_beta_testers
...@@ -63,13 +64,13 @@ from courseware.url_helpers import get_redirect_url ...@@ -63,13 +64,13 @@ from courseware.url_helpers import get_redirect_url
from courseware.user_state_client import DjangoXBlockUserStateClient from courseware.user_state_client import DjangoXBlockUserStateClient
from edxmako.shortcuts import render_to_response, render_to_string, marketing_link from edxmako.shortcuts import render_to_response, render_to_string, marketing_link
from instructor.enrollment import uses_shib from instructor.enrollment import uses_shib
from microsite_configuration import microsite
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.credit.api import ( from openedx.core.djangoapps.credit.api import (
get_credit_requirement_status, get_credit_requirement_status,
is_user_eligible_for_credit, is_user_eligible_for_credit,
is_credit_course is_credit_course
) )
from openedx.core.djangoapps.theming import helpers as theming_helpers
from shoppingcart.models import CourseRegistrationCode from shoppingcart.models import CourseRegistrationCode
from shoppingcart.utils import is_shopping_cart_enabled from shoppingcart.utils import is_shopping_cart_enabled
from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
...@@ -143,8 +144,10 @@ def courses(request): ...@@ -143,8 +144,10 @@ def courses(request):
if not settings.FEATURES.get('ENABLE_COURSE_DISCOVERY'): if not settings.FEATURES.get('ENABLE_COURSE_DISCOVERY'):
courses_list = get_courses(request.user) courses_list = get_courses(request.user)
if microsite.get_value("ENABLE_COURSE_SORTING_BY_START_DATE", if theming_helpers.get_value(
settings.FEATURES["ENABLE_COURSE_SORTING_BY_START_DATE"]): "ENABLE_COURSE_SORTING_BY_START_DATE",
settings.FEATURES["ENABLE_COURSE_SORTING_BY_START_DATE"]
):
courses_list = sort_by_start_date(courses_list) courses_list = sort_by_start_date(courses_list)
else: else:
courses_list = sort_by_announcement(courses_list) courses_list = sort_by_announcement(courses_list)
...@@ -508,7 +511,7 @@ def _index_bulk_op(request, course_key, chapter, section, position): ...@@ -508,7 +511,7 @@ def _index_bulk_op(request, course_key, chapter, section, position):
return redirect(reverse('courseware', args=[course.id.to_deprecated_string()])) return redirect(reverse('courseware', args=[course.id.to_deprecated_string()]))
raise Http404 raise Http404
## Allow chromeless operation # Allow chromeless operation
if section_descriptor.chrome: if section_descriptor.chrome:
chrome = [s.strip() for s in section_descriptor.chrome.lower().split(",")] chrome = [s.strip() for s in section_descriptor.chrome.lower().split(",")]
if 'accordion' not in chrome: if 'accordion' not in chrome:
...@@ -855,8 +858,9 @@ def course_about(request, course_id): ...@@ -855,8 +858,9 @@ def course_about(request, course_id):
with modulestore().bulk_operations(course_key): with modulestore().bulk_operations(course_key):
permission = get_permission_for_course_about() permission = get_permission_for_course_about()
course = get_course_with_access(request.user, permission, course_key) course = get_course_with_access(request.user, permission, course_key)
modes = CourseMode.modes_for_course_dict(course_key)
if microsite.get_value('ENABLE_MKTG_SITE', settings.FEATURES.get('ENABLE_MKTG_SITE', False)): if theming_helpers.get_value('ENABLE_MKTG_SITE', settings.FEATURES.get('ENABLE_MKTG_SITE', False)):
return redirect(reverse('info', args=[course.id.to_deprecated_string()])) return redirect(reverse('info', args=[course.id.to_deprecated_string()]))
registered = registered_for_course(course, request.user) registered = registered_for_course(course, request.user)
...@@ -871,10 +875,9 @@ def course_about(request, course_id): ...@@ -871,10 +875,9 @@ def course_about(request, course_id):
show_courseware_link = bool( show_courseware_link = bool(
( (
has_access(request.user, 'load', course) has_access(request.user, 'load', course) and
and has_access(request.user, 'view_courseware_with_prerequisites', course) has_access(request.user, 'view_courseware_with_prerequisites', course)
) ) or settings.FEATURES.get('ENABLE_LMS_MIGRATION')
or settings.FEATURES.get('ENABLE_LMS_MIGRATION')
) )
# Note: this is a flow for payment for course registration, not the Verified Certificate flow. # Note: this is a flow for payment for course registration, not the Verified Certificate flow.
...@@ -884,15 +887,31 @@ def course_about(request, course_id): ...@@ -884,15 +887,31 @@ def course_about(request, course_id):
_is_shopping_cart_enabled = is_shopping_cart_enabled() _is_shopping_cart_enabled = is_shopping_cart_enabled()
if _is_shopping_cart_enabled: if _is_shopping_cart_enabled:
registration_price = CourseMode.min_course_price_for_currency(course_key, registration_price = CourseMode.min_course_price_for_currency(
settings.PAID_COURSE_REGISTRATION_CURRENCY[0]) course_key,
settings.PAID_COURSE_REGISTRATION_CURRENCY[0]
)
if request.user.is_authenticated(): if request.user.is_authenticated():
cart = shoppingcart.models.Order.get_cart_for_user(request.user) cart = shoppingcart.models.Order.get_cart_for_user(request.user)
in_cart = shoppingcart.models.PaidCourseRegistration.contained_in_order(cart, course_key) or \ in_cart = shoppingcart.models.PaidCourseRegistration.contained_in_order(cart, course_key) or \
shoppingcart.models.CourseRegCodeItem.contained_in_order(cart, course_key) shoppingcart.models.CourseRegCodeItem.contained_in_order(cart, course_key)
reg_then_add_to_cart_link = "{reg_url}?course_id={course_id}&enrollment_action=add_to_cart".format( reg_then_add_to_cart_link = "{reg_url}?course_id={course_id}&enrollment_action=add_to_cart".format(
reg_url=reverse('register_user'), course_id=urllib.quote(str(course_id))) reg_url=reverse('register_user'), course_id=urllib.quote(str(course_id))
)
# If the ecommerce checkout flow is enabled and the mode of the course is
# professional or no id professional, we construct links for the enrollment
# button to add the course to the ecommerce basket.
ecommerce_checkout_link = ''
professional_mode = ''
ecomm_service = EcommerceService()
if ecomm_service.is_enabled() and (
CourseMode.PROFESSIONAL in modes or CourseMode.NO_ID_PROFESSIONAL_MODE in modes
):
professional_mode = modes.get(CourseMode.PROFESSIONAL, '') or \
modes.get(CourseMode.NO_ID_PROFESSIONAL_MODE, '')
ecommerce_checkout_link = ecomm_service.checkout_page_url(professional_mode.sku)
course_price = get_cosmetic_display_price(course, registration_price) course_price = get_cosmetic_display_price(course, registration_price)
can_add_course_to_cart = _is_shopping_cart_enabled and registration_price can_add_course_to_cart = _is_shopping_cart_enabled and registration_price
...@@ -925,6 +944,9 @@ def course_about(request, course_id): ...@@ -925,6 +944,9 @@ def course_about(request, course_id):
'is_cosmetic_price_enabled': settings.FEATURES.get('ENABLE_COSMETIC_DISPLAY_PRICE'), 'is_cosmetic_price_enabled': settings.FEATURES.get('ENABLE_COSMETIC_DISPLAY_PRICE'),
'course_price': course_price, 'course_price': course_price,
'in_cart': in_cart, 'in_cart': in_cart,
'ecommerce_checkout': ecomm_service.is_enabled(),
'ecommerce_checkout_link': ecommerce_checkout_link,
'professional_mode': professional_mode,
'reg_then_add_to_cart_link': reg_then_add_to_cart_link, 'reg_then_add_to_cart_link': reg_then_add_to_cart_link,
'show_courseware_link': show_courseware_link, 'show_courseware_link': show_courseware_link,
'is_course_full': is_course_full, 'is_course_full': is_course_full,
...@@ -1577,12 +1599,12 @@ def financial_assistance_form(request): ...@@ -1577,12 +1599,12 @@ def financial_assistance_form(request):
enrolled_courses = [ enrolled_courses = [
{'name': enrollment.course_overview.display_name, 'value': unicode(enrollment.course_id)} {'name': enrollment.course_overview.display_name, 'value': unicode(enrollment.course_id)}
for enrollment in CourseEnrollment.enrollments_for_user(user).order_by('-created') for enrollment in CourseEnrollment.enrollments_for_user(user).order_by('-created')
if CourseMode.objects.filter(
if enrollment.mode != CourseMode.VERIFIED and CourseMode.objects.filter(
Q(_expiration_datetime__isnull=True) | Q(_expiration_datetime__gt=datetime.now(UTC())), Q(_expiration_datetime__isnull=True) | Q(_expiration_datetime__gt=datetime.now(UTC())),
course_id=enrollment.course_id, course_id=enrollment.course_id,
mode_slug=CourseMode.VERIFIED mode_slug=CourseMode.VERIFIED
).exists() ).exists()
and enrollment.mode != CourseMode.VERIFIED
] ]
return render_to_response('financial-assistance/apply.html', { return render_to_response('financial-assistance/apply.html', {
'header_text': FINANCIAL_ASSISTANCE_HEADER, 'header_text': FINANCIAL_ASSISTANCE_HEADER,
......
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
enrollmentAction: $.url( '?enrollment_action' ), enrollmentAction: $.url( '?enrollment_action' ),
courseId: $.url( '?course_id' ), courseId: $.url( '?course_id' ),
courseMode: $.url( '?course_mode' ), courseMode: $.url( '?course_mode' ),
emailOptIn: $.url( '?email_opt_in') emailOptIn: $.url( '?email_opt_in' )
}; };
for (var key in queryParams) { for (var key in queryParams) {
if (queryParams[key]) { if (queryParams[key]) {
......
...@@ -21,7 +21,7 @@ from django.core.urlresolvers import reverse ...@@ -21,7 +21,7 @@ from django.core.urlresolvers import reverse
} else { } else {
title.attr("aria-expanded", "false"); title.attr("aria-expanded", "false");
} }
} };
$(document).ready(function() { $(document).ready(function() {
$('.expandable-area').slideUp(); $('.expandable-area').slideUp();
...@@ -38,6 +38,12 @@ from django.core.urlresolvers import reverse ...@@ -38,6 +38,12 @@ from django.core.urlresolvers import reverse
$('#contribution-other').attr('checked',true); $('#contribution-other').attr('checked',true);
}); });
% if use_ecommerce_payment_flow:
$('input[name=verified_mode]').click(function(e){
e.preventDefault();
window.location.href = '${ecommerce_payment_page}?sku=${sku}';
});
% endif
}); });
</script> </script>
</%block> </%block>
......
...@@ -39,6 +39,7 @@ from openedx.core.lib.courses import course_image_url ...@@ -39,6 +39,7 @@ from openedx.core.lib.courses import course_image_url
location.href = "${reg_then_add_to_cart_link}"; location.href = "${reg_then_add_to_cart_link}";
} }
}; };
$("#add_to_cart_post").click(function(event){ $("#add_to_cart_post").click(function(event){
$.ajax({ $.ajax({
url: "${reverse('add_course_to_cart', args=[course.id.to_deprecated_string()])}", url: "${reverse('add_course_to_cart', args=[course.id.to_deprecated_string()])}",
...@@ -152,14 +153,27 @@ from openedx.core.lib.courses import course_image_url ...@@ -152,14 +153,27 @@ from openedx.core.lib.courses import course_image_url
reg_href = reg_then_add_to_cart_link reg_href = reg_then_add_to_cart_link
reg_element_id = "reg_then_add_to_cart" reg_element_id = "reg_then_add_to_cart"
%> %>
<% if ecommerce_checkout:
reg_href = ecommerce_checkout_link
reg_element_id = ""
%>
<a href="${reg_href}" class="add-to-cart" id="${reg_element_id}"> <a href="${reg_href}" class="add-to-cart" id="${reg_element_id}">
${_("Add {course_name} to Cart <span>({price} USD)</span>")\ ${_("Add {course_name} to Cart <span>({price} USD)</span>")\
.format(course_name=course.display_number_with_default, price=course_price)} .format(course_name=course.display_number_with_default, price=course_price)}
</a> </a>
<div id="register_error"></div> <div id="register_error"></div>
%else: %else:
<a href="#" class="register"> <%
if ecommerce_checkout:
reg_href = ecommerce_checkout_link
else:
reg_href="#"
if professional_mode:
href_class = "add-to-cart"
else:
href_class = "register"
%>
<a href="${reg_href}" class="${href_class}">
${_("Enroll in {course_name}").format(course_name=course.display_number_with_default) | h} ${_("Enroll in {course_name}").format(course_name=course.display_number_with_default) | h}
</a> </a>
<div id="register_error"></div> <div id="register_error"></div>
......
...@@ -330,7 +330,11 @@ from student.helpers import ( ...@@ -330,7 +330,11 @@ from student.helpers import (
${_("It's official. It's easily shareable. It's a proven motivator to complete the course. <br>{link_start}Learn more about the verified {cert_name_long}{link_end}.").format(link_start='<a href="{}" class="verified-info" data-course-key="{}">'.format(marketing_link('WHAT_IS_VERIFIED_CERT'), enrollment.course_id), link_end="</a>", cert_name_long=cert_name_long)} ${_("It's official. It's easily shareable. It's a proven motivator to complete the course. <br>{link_start}Learn more about the verified {cert_name_long}{link_end}.").format(link_start='<a href="{}" class="verified-info" data-course-key="{}">'.format(marketing_link('WHAT_IS_VERIFIED_CERT'), enrollment.course_id), link_end="</a>", cert_name_long=cert_name_long)}
</p> </p>
<div class="action-upgrade-container"> <div class="action-upgrade-container">
<a class="action action-upgrade" href="${reverse('verify_student_upgrade_and_verify', kwargs={'course_id': unicode(course_overview.id)})}" data-course-id="${course_overview.id | h}" data-user="${user.username | h}"> % if use_ecommerce_payment_flow and course_mode_info['verified_sku']:
<a class="action action-upgrade" href="${ecommerce_payment_page}?sku=${course_mode_info['verified_sku']}">
% else:
<a class="action action-upgrade" href="${reverse('verify_student_upgrade_and_verify', kwargs={'course_id': unicode(course_overview.id)})}" data-course-id="${course_overview.id | h}" data-user="${user.username | h}">
% endif
<i class="action-upgrade-icon"></i> <i class="action-upgrade-icon"></i>
<span class="wrapper-copy"> <span class="wrapper-copy">
<span class="copy" id="upgrade-to-verified">${_("Upgrade to Verified")}</span> <span class="copy" id="upgrade-to-verified">${_("Upgrade to Verified")}</span>
......
...@@ -21,7 +21,7 @@ from django.core.urlresolvers import reverse ...@@ -21,7 +21,7 @@ from django.core.urlresolvers import reverse
} else { } else {
title.attr("aria-expanded", "false"); title.attr("aria-expanded", "false");
} }
} };
$(document).ready(function() { $(document).ready(function() {
$('.expandable-area').slideUp(); $('.expandable-area').slideUp();
...@@ -38,6 +38,12 @@ from django.core.urlresolvers import reverse ...@@ -38,6 +38,12 @@ from django.core.urlresolvers import reverse
$('#contribution-other').attr('checked',true); $('#contribution-other').attr('checked',true);
}); });
% if use_ecommerce_payment_flow:
$('input[name=verified_mode]').click(function(e){
e.preventDefault();
window.location.href = '${ecommerce_payment_page}?sku=${sku}';
});
% endif
}); });
</script> </script>
</%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