Commit 43d8c7a7 by David Ormsbee

Merge branch 'release'

Conflicts:
	lms/djangoapps/shoppingcart/processors/CyberSource2.py
parents a0a5746e 1a4ad061
......@@ -94,6 +94,7 @@ class ChooseModeView(View):
"error": error,
"upgrade": upgrade,
"can_audit": "audit" in modes,
"responsive": True
}
if "verified" in modes:
context["suggested_prices"] = [
......
......@@ -485,6 +485,7 @@ class LoginOAuthTokenMixin(object):
self._setup_user_response(success=True)
response = self.client.post(self.url, {"access_token": "dummy"})
self.assertEqual(response.status_code, 204)
self.assertEqual(self.client.session['_auth_user_id'], self.user.id)
def test_invalid_token(self):
self._setup_user_response(success=False)
......
......@@ -265,6 +265,7 @@ class DashboardTest(ModuleStoreTestCase):
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
@patch('courseware.views.log.warning')
@patch.dict('django.conf.settings.FEATURES', {'ENABLE_PAID_COURSE_REGISTRATION': True})
def test_blocked_course_scenario(self, log_warning):
self.client.login(username="jack", password="test")
......
......@@ -1114,6 +1114,7 @@ def login_user(request, error=""): # pylint: disable-msg=too-many-statements,un
}) # TODO: this should be status code 400 # pylint: disable=fixme
@csrf_exempt
@require_POST
@social_utils.strategy("social:complete")
def login_oauth_token(request, backend):
......@@ -1134,6 +1135,7 @@ def login_oauth_token(request, backend):
pass
# do_auth can return a non-User object if it fails
if user and isinstance(user, User):
login(request, user)
return JsonResponse(status=204)
else:
# Ensure user does not re-enter the pipeline
......
/**
* Adds rwd classes and click handlers.
*/
(function($) {
'use strict';
var rwd = (function() {
var _fn = {
header: 'header.global-new',
footer: '.edx-footer-new',
resultsUrl: 'course-search',
init: function() {
_fn.$header = $( _fn.header );
_fn.$footer = $( _fn.footer );
_fn.$nav = _fn.$header.find('nav');
_fn.$globalNav = _fn.$nav.find('.nav-global');
_fn.add.elements();
_fn.add.classes();
_fn.eventHandlers.init();
},
add: {
classes: function() {
// Add any RWD-specific classes
_fn.$header.addClass('rwd');
_fn.$footer.addClass('rwd');
},
elements: function() {
_fn.add.burger();
_fn.add.registerLink();
},
burger: function() {
_fn.$nav.prepend([
'<a href="#" class="mobile-menu-button" aria-label="menu">',
'<i class="icon-reorder" aria-hidden="true"></i>',
'</a>'
].join(''));
},
registerLink: function() {
var $register = _fn.$nav.find('.cta-register'),
$li = {},
$a = {},
count = 0;
// Add if register link is shown
if ( $register.length > 0 ) {
count = _fn.$globalNav.find('li').length + 1;
// Create new li
$li = $('<li/>');
$li.addClass('desktop-hide nav-global-0' + count);
// Clone register link and remove classes
$a = $register.clone();
$a.removeClass();
// append to DOM
$a.appendTo( $li );
_fn.$globalNav.append( $li );
}
}
},
eventHandlers: {
init: function() {
_fn.eventHandlers.click();
},
click: function() {
// Toggle menu
_fn.$nav.on( 'click', '.mobile-menu-button', _fn.toggleMenu );
}
},
toggleMenu: function( event ) {
event.preventDefault();
_fn.$globalNav.toggleClass('show');
}
};
return {
init: _fn.init
};
})();
setTimeout( function() {
rwd.init();
}, 100);
})(jQuery);
......@@ -10,7 +10,7 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from helpers import LoginEnrollmentTestCase
from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE
from course_modes.models import CourseMode
from xmodule.course_module import (
CATALOG_VISIBILITY_CATALOG_AND_ABOUT, CATALOG_VISIBILITY_NONE)
......@@ -56,6 +56,7 @@ class TestMicrosites(ModuleStoreTestCase, LoginEnrollmentTestCase):
self.course_with_visibility = CourseFactory.create(
display_name='visible_course',
org='TestMicrositeX',
course="foo",
catalog_visibility=CATALOG_VISIBILITY_CATALOG_AND_ABOUT,
)
......@@ -190,3 +191,33 @@ class TestMicrosites(ModuleStoreTestCase, LoginEnrollmentTestCase):
url = reverse('about_course', args=[self.course_hidden_visibility.id.to_deprecated_string()])
resp = self.client.get(url, HTTP_HOST=settings.MICROSITE_TEST_HOSTNAME)
self.assertEqual(resp.status_code, 404)
@override_settings(SITE_NAME=settings.MICROSITE_TEST_HOSTNAME)
def test_paid_course_registration(self):
"""
Make sure that Microsite overrides on the ENABLE_SHOPPING_CART and
ENABLE_PAID_COURSE_ENROLLMENTS are honored
"""
course_mode = CourseMode(
course_id=self.course_with_visibility.id,
mode_slug="honor",
mode_display_name="honor cert",
min_price=10,
)
course_mode.save()
# first try on the non microsite, which
# should pick up the global configuration (where ENABLE_PAID_COURSE_REGISTRATIONS = False)
url = reverse('about_course', args=[self.course_with_visibility.id.to_deprecated_string()])
resp = self.client.get(url)
self.assertEqual(resp.status_code, 200)
self.assertIn("Register for {}".format(self.course_with_visibility.id.course), resp.content)
self.assertNotIn("Add {} to Cart ($10)".format(self.course_with_visibility.id.course), resp.content)
# now try on the microsite
url = reverse('about_course', args=[self.course_with_visibility.id.to_deprecated_string()])
resp = self.client.get(url, HTTP_HOST=settings.MICROSITE_TEST_HOSTNAME)
self.assertEqual(resp.status_code, 200)
self.assertNotIn("Register for {}".format(self.course_with_visibility.id.course), resp.content)
self.assertIn("Add {} to Cart ($10)".format(self.course_with_visibility.id.course), resp.content)
self.assertIn('$("#add_to_cart_post").click', resp.content)
......@@ -49,6 +49,7 @@ from xmodule.tabs import CourseTabList, StaffGradingTab, PeerGradingTab, OpenEnd
from xmodule.x_module import STUDENT_VIEW
import shoppingcart
from shoppingcart.models import CourseRegistrationCode
from shoppingcart.utils import is_shopping_cart_enabled
from opaque_keys import InvalidKeyError
from microsite_configuration import microsite
......@@ -731,8 +732,9 @@ def course_about(request, course_id):
registration_price = 0
in_cart = False
reg_then_add_to_cart_link = ""
if (settings.FEATURES.get('ENABLE_SHOPPING_CART') and
settings.FEATURES.get('ENABLE_PAID_COURSE_REGISTRATION')):
_is_shopping_cart_enabled = is_shopping_cart_enabled()
if (_is_shopping_cart_enabled):
registration_price = CourseMode.min_course_price_for_currency(course_key,
settings.PAID_COURSE_REGISTRATION_CURRENCY[0])
if request.user.is_authenticated():
......@@ -774,6 +776,8 @@ def course_about(request, course_id):
# We do not want to display the internal courseware header, which is used when the course is found in the
# context. This value is therefor explicitly set to render the appropriate header.
'disable_courseware_header': True,
'is_shopping_cart_enabled': _is_shopping_cart_enabled,
'cart_link': reverse('shoppingcart.views.show_cart'),
})
......
......@@ -1636,6 +1636,7 @@ class TestInstructorAPILevelsAccess(ModuleStoreTestCase, LoginEnrollmentTestCase
@ddt.ddt
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
@patch.dict('django.conf.settings.FEATURES', {'ENABLE_PAID_COURSE_REGISTRATION': True})
class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCase):
"""
Test endpoints that show data without side effects.
......
......@@ -5,9 +5,9 @@ navigation. We want to do this in the context_processor to
1) keep database accesses out of templates (this led to a transaction bug with user email changes)
2) because navigation.html is "called" by being included in other templates, there's no "views.py" to put this.
"""
from django.conf import settings
import shoppingcart
from microsite_configuration import microsite
from .models import Order, PaidCourseRegistration, CourseRegCodeItem
from .utils import is_shopping_cart_enabled
def user_has_cart_context_processor(request):
......@@ -19,20 +19,12 @@ def user_has_cart_context_processor(request):
display_shopping_cart = (
# user is logged in and
request.user.is_authenticated() and
# settings enable paid course reg
microsite.get_value(
'ENABLE_PAID_COURSE_REGISTRATION',
settings.FEATURES.get('ENABLE_PAID_COURSE_REGISTRATION')
) and
# settings enable shopping cart
microsite.get_value(
'ENABLE_SHOPPING_CART',
settings.FEATURES.get('ENABLE_SHOPPING_CART')
) and
# do we have the feature turned on
is_shopping_cart_enabled() and
# user's cart has PaidCourseRegistrations
shoppingcart.models.Order.user_cart_has_items(
Order.user_cart_has_items(
request.user,
[shoppingcart.models.PaidCourseRegistration, shoppingcart.models.CourseRegCodeItem]
[PaidCourseRegistration, CourseRegCodeItem]
)
)
......
"""
This file defines any decorators used by the shopping cart app
"""
from django.http import Http404
from .utils import is_shopping_cart_enabled
def enforce_shopping_cart_enabled(func):
"""
Is a decorator that forces a wrapped method to be run in a runtime
which has the ENABLE_SHOPPING_CART flag set
"""
def func_wrapper(*args, **kwargs):
"""
Wrapper function that does the enforcement that
the shopping cart feature is enabled
"""
if not is_shopping_cart_enabled():
raise Http404
return func(*args, **kwargs)
return func_wrapper
......@@ -352,6 +352,9 @@ class Order(models.Model):
"""
if self.status == 'purchased':
log.error(
u"`purchase` method called on order {}, but order is already purchased.".format(self.id) # pylint: disable=E1101
)
return
self.status = 'purchased'
self.purchase_time = datetime.now(pytz.utc)
......
......@@ -367,6 +367,8 @@ def _payment_accepted(order_id, auth_amount, currency, decision):
total_cost_currency=order.currency
)
)
#pylint: disable=attribute-defined-outside-init
ex.order = order
raise ex
else:
......
......@@ -64,6 +64,7 @@ MODULESTORE_CONFIG = mixed_store_config(settings.COMMON_TEST_DATA_ROOT, {}, incl
@override_settings(MODULESTORE=MODULESTORE_CONFIG)
@patch.dict('django.conf.settings.FEATURES', {'ENABLE_PAID_COURSE_REGISTRATION': True})
class ShoppingCartViewsTests(ModuleStoreTestCase):
def setUp(self):
patcher = patch('student.models.tracker')
......@@ -963,8 +964,36 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
((template, _context), _tmp) = render_mock.call_args
self.assertEqual(template, cert_item.single_item_receipt_template)
def _assert_404(self, url, use_post=False):
"""
Helper method to assert that a given url will return a 404 status code
"""
if use_post:
response = self.client.post(url)
else:
response = self.client.get(url)
self.assertEquals(response.status_code, 404)
@patch.dict('django.conf.settings.FEATURES', {'ENABLE_PAID_COURSE_REGISTRATION': False})
def test_disabled_paid_courses(self):
"""
Assert that the pages that require ENABLE_PAID_COURSE_REGISTRATION=True return a
HTTP 404 status code when we have this flag turned off
"""
self.login_user()
self._assert_404(reverse('shoppingcart.views.show_cart', args=[]))
self._assert_404(reverse('shoppingcart.views.clear_cart', args=[]))
self._assert_404(reverse('shoppingcart.views.remove_item', args=[]), use_post=True)
self._assert_404(reverse('shoppingcart.views.register_code_redemption', args=["testing"]))
self._assert_404(reverse('shoppingcart.views.use_code', args=[]), use_post=True)
self._assert_404(reverse('shoppingcart.views.update_user_cart', args=[]))
self._assert_404(reverse('shoppingcart.views.reset_code_redemption', args=[]), use_post=True)
self._assert_404(reverse('shoppingcart.views.billing_details', args=[]))
self._assert_404(reverse('shoppingcart.views.register_courses', args=[]))
@override_settings(MODULESTORE=MODULESTORE_CONFIG)
@patch.dict('django.conf.settings.FEATURES', {'ENABLE_PAID_COURSE_REGISTRATION': True})
class RegistrationCodeRedemptionCourseEnrollment(ModuleStoreTestCase):
"""
Test suite for RegistrationCodeRedemption Course Enrollments
......
......@@ -6,23 +6,19 @@ urlpatterns = patterns('shoppingcart.views', # nopep8
url(r'^receipt/(?P<ordernum>[0-9]*)/$', 'show_receipt'),
url(r'^donation/$', 'donate', name='donation'),
url(r'^csv_report/$', 'csv_report', name='payment_csv_report'),
# These following URLs are only valid if the ENABLE_SHOPPING_CART feature flag is set
url(r'^$', 'show_cart'),
url(r'^clear/$', 'clear_cart'),
url(r'^remove_item/$', 'remove_item'),
url(r'^add/course/{}/$'.format(settings.COURSE_ID_PATTERN), 'add_course_to_cart', name='add_course_to_cart'),
url(r'^register/redeem/(?P<registration_code>[0-9A-Za-z]+)/$', 'register_code_redemption', name='register_code_redemption'),
url(r'^use_code/$', 'use_code'),
url(r'^update_user_cart/$', 'update_user_cart'),
url(r'^reset_code_redemption/$', 'reset_code_redemption'),
url(r'^billing_details/$', 'billing_details', name='billing_details'),
url(r'^register_courses/$', 'register_courses'),
)
if settings.FEATURES['ENABLE_SHOPPING_CART']:
urlpatterns += patterns(
'shoppingcart.views',
url(r'^$', 'show_cart'),
url(r'^clear/$', 'clear_cart'),
url(r'^remove_item/$', 'remove_item'),
url(r'^add/course/{}/$'.format(settings.COURSE_ID_PATTERN), 'add_course_to_cart', name='add_course_to_cart'),
url(r'^register/redeem/(?P<registration_code>[0-9A-Za-z]+)/$', 'register_code_redemption', name='register_code_redemption'),
url(r'^use_code/$', 'use_code'),
url(r'^update_user_cart/$', 'update_user_cart'),
url(r'^reset_code_redemption/$', 'reset_code_redemption'),
url(r'^billing_details/$', 'billing_details', name='billing_details'),
url(r'^register_courses/$', 'register_courses'),
)
if settings.FEATURES.get('ENABLE_PAYMENT_FAKE'):
from shoppingcart.tests.payment_fake import PaymentFakeView
urlpatterns += patterns(
......
"""
Utility methods for the Shopping Cart app
"""
from django.conf import settings
from microsite_configuration import microsite
def is_shopping_cart_enabled():
"""
Utility method to check the various configuration to verify that
all of the settings have been enabled
"""
enable_paid_course_registration = microsite.get_value(
'ENABLE_PAID_COURSE_REGISTRATION',
settings.FEATURES.get('ENABLE_PAID_COURSE_REGISTRATION')
)
enable_shopping_cart = microsite.get_value(
'ENABLE_SHOPPING_CART',
settings.FEATURES.get('ENABLE_SHOPPING_CART')
)
return (enable_paid_course_registration and enable_shopping_cart)
......@@ -33,7 +33,7 @@ from .exceptions import (
)
from .models import (
Order, OrderTypes,
PaidCourseRegistration, OrderItem, Coupon, CourseRegCodeItem,
PaidCourseRegistration, OrderItem, Coupon,
CouponRedemption, CourseRegistrationCode, RegistrationCodeRedemption,
Donation, DonationConfiguration
)
......@@ -44,6 +44,7 @@ from .processors import (
import json
from xmodule_django.models import CourseKeyField
from .decorators import enforce_shopping_cart_enabled
log = logging.getLogger("shoppingcart")
AUDIT_LOG = logging.getLogger("audit")
......@@ -94,6 +95,7 @@ def add_course_to_cart(request, course_id):
@login_required
@enforce_shopping_cart_enabled
def update_user_cart(request):
"""
when user change the number-of-students from the UI then
......@@ -127,6 +129,7 @@ def update_user_cart(request):
@login_required
@enforce_shopping_cart_enabled
def show_cart(request):
"""
This view shows cart items.
......@@ -158,6 +161,7 @@ def show_cart(request):
@login_required
@enforce_shopping_cart_enabled
def clear_cart(request):
cart = Order.get_cart_for_user(request.user)
cart.clear()
......@@ -175,6 +179,7 @@ def clear_cart(request):
@login_required
@enforce_shopping_cart_enabled
def remove_item(request):
"""
This will remove an item from the user cart and also delete the corresponding coupon codes redemption.
......@@ -228,6 +233,7 @@ def remove_code_redemption(order_item_course_id, item_id, item, user):
@login_required
@enforce_shopping_cart_enabled
def reset_code_redemption(request):
"""
This method reset the code redemption from user cart items.
......@@ -240,6 +246,7 @@ def reset_code_redemption(request):
@login_required
@enforce_shopping_cart_enabled
def use_code(request):
"""
This method may generate the discount against valid coupon code
......@@ -292,6 +299,7 @@ def get_reg_code_validity(registration_code, request, limiter):
@require_http_methods(["GET", "POST"])
@login_required
@enforce_shopping_cart_enabled
def register_code_redemption(request, registration_code):
"""
This view allows the student to redeem the registration code
......@@ -383,6 +391,7 @@ def use_coupon_code(coupons, user):
@login_required
@enforce_shopping_cart_enabled
def register_courses(request):
"""
This method enroll the user for available course(s)
......@@ -519,6 +528,7 @@ def postpay_callback(request):
@require_http_methods(["GET", "POST"])
@login_required
@enforce_shopping_cart_enabled
def billing_details(request):
"""
This is the view for capturing additional billing details
......
......@@ -1026,6 +1026,7 @@ main_vendor_js = base_vendor_js + [
dashboard_js = sorted(rooted_glob(PROJECT_ROOT / 'static', 'js/dashboard/**/*.js'))
discussion_js = sorted(rooted_glob(COMMON_ROOT / 'static', 'coffee/src/discussion/**/*.js'))
rwd_header_footer_js = sorted(rooted_glob(PROJECT_ROOT / 'static', 'js/common_helpers/rwd_header_footer.js'))
staff_grading_js = sorted(rooted_glob(PROJECT_ROOT / 'static', 'coffee/src/staff_grading/**/*.js'))
open_ended_js = sorted(rooted_glob(PROJECT_ROOT / 'static', 'coffee/src/open_ended/**/*.js'))
notes_js = sorted(rooted_glob(PROJECT_ROOT / 'static', 'coffee/src/notes/**/*.js'))
......@@ -1204,6 +1205,10 @@ PIPELINE_JS = {
'source_filenames': dashboard_js,
'output_filename': 'js/dashboard.js'
},
'rwd_header_footer': {
'source_filenames': rwd_header_footer_js,
'output_filename': 'js/rwd_header_footer.js'
},
'student_account': {
'source_filenames': student_account_js,
'output_filename': 'js/student_account.js'
......
......@@ -352,6 +352,8 @@ MICROSITE_CONFIGURATION = {
"ALWAYS_REDIRECT_HOMEPAGE_TO_DASHBOARD_FOR_AUTHENTICATED_USER": False,
"COURSE_CATALOG_VISIBILITY_PERMISSION": "see_in_catalog",
"COURSE_ABOUT_VISIBILITY_PERMISSION": "see_about_page",
"ENABLE_SHOPPING_CART": True,
"ENABLE_PAID_COURSE_REGISTRATION": True,
},
"default": {
"university": "default_university",
......
@import "neat/neat-helpers"; // or "neat-helpers" when in Rails
/* Change the grid settings */
$max-width: 1200px;
/* Override the default global box-sizing */
$border-box-sizing: false;
/* Breakpoints */
$mobile: new-breakpoint(max-width 320px 4);
$tablet: new-breakpoint(min-width 321px max-width 768px, 8);
$desktop: new-breakpoint(min-width 769px 12);
$xl-desktop: new-breakpoint(min-width 980px 12);
......@@ -173,6 +173,7 @@ $m-blue-d1: #1790C7;
$m-blue-d2: #1580B0;
$m-blue-d3: #126F9A;
$m-blue-d4: #0A4A67;
$m-blue-d5: #009EE7;
$m-blue-t0: rgba($m-blue,0.125);
$m-blue-t1: rgba($m-blue,0.25);
$m-blue-t2: rgba($m-blue,0.50);
......@@ -423,6 +424,7 @@ $header-sans-serif: 'Open Sans', Arial, Helvetica, sans-serif;
$msg-bg: $action-primary-bg;
// New Shopping Cart
$dark-gray1: #4a4a4a;
......
// Open edX: LMS footer
// ====================
@import '../base/grid-settings';
@import 'neat/neat'; // lib - Neat
.wrapper-footer {
box-shadow: 0 -1px 5px 0 rgba(0,0,0, 0.1);
border-top: 1px solid tint($m-gray,50%);
padding: 25px ($baseline/2) ($baseline*1.5) ($baseline/2);
background: $footer-bg;
clear: both;
footer {
@include clearfix();
......@@ -280,8 +284,6 @@ $edx-footer-bg-color: rgb(252,252,252);
@extend %t-weight4;
}
}
}
.edx-footer-new {
......@@ -352,6 +354,7 @@ $edx-footer-bg-color: rgb(252,252,252);
.footer-nav-title {
@extend %edx-footer-title;
margin-top: $baseline;
}
.footer-nav-links {
......@@ -372,12 +375,14 @@ $edx-footer-bg-color: rgb(252,252,252);
.footer-follow-title {
@extend %edx-footer-title;
margin-top: $baseline;
}
.footer-follow-links {
a {
@extend %edx-footer-link;
margin-top: 20px;
.icon, .copy {
display: inline-block;
......@@ -397,4 +402,33 @@ $edx-footer-bg-color: rgb(252,252,252);
}
}
}
&.rwd {
@include box-sizing(border-box);
@include outer-container;
&.wrapper-footer footer {
min-width: 0;
}
.footer-about,
.footer-nav,
.footer-follow {
@include span-columns(12);
}
@include media( $tablet ) {
}
@include media( $desktop ) {
.footer-about {
@include span-columns(6);
}
.footer-nav,
.footer-follow {
@include span-columns(3);
}
}
}
}
@import '../base/grid-settings';
@import 'neat/neat'; // lib - Neat
header.global {
border-bottom: 1px solid $m-gray;
box-shadow: 0 1px 5px 0 rgba(0,0,0, 0.1);
......@@ -317,7 +320,6 @@ header.global {
.view-courses .nav-global-02,
.view-schools .nav-global-03,
.view-register .nav-global-04 {
a {
text-decoration: none;
color: $link-color !important;
......@@ -331,8 +333,10 @@ header.global {
// CASE: marketing/course discovery
header.global-new {
@extend %ui-depth1;
/* Temp. fix until applied globally */
@include box-sizing(border-box);
position: relative;
height: ($baseline*3.75);
width: 100%;
border-bottom: 4px solid $courseware-border-bottom-color;
box-shadow: 0 1px 5px 0 rgba(0,0,0, 0.1);
......@@ -340,15 +344,16 @@ header.global-new {
nav {
@include clearfix();
@include box-sizing(border-box);
width: grid-width(12);
height: ($baseline*2);
height: 74px;
margin: 0 auto;
padding: 18px ($baseline/2) 0;
padding: 17px 0;
}
h1.logo {
float: left;
margin: -2px 39px 0px 0px;
margin: -2px 39px 0 10px;
position: relative;
a {
......@@ -560,7 +565,7 @@ header.global-new {
}
}
.nav-global {
%default-header-nav {
margin-top: ($baseline/4);
list-style: none;
float: left;
......@@ -568,25 +573,21 @@ header.global-new {
li,
div {
display: inline-block;
margin: 0 $baseline+1 0 0;
margin: 0;
text-transform: uppercase;
letter-spacing: 0 !important;
&:last-child {
margin-right: 0;
}
a {
border-bottom: 4px solid $header-bg;
display:block;
padding: ($baseline/4);
padding: 3px 10px;
font-size: 18px;
padding-bottom: ($baseline*1.25);
font-weight: 600;
line-height: 24px;
font-weight: 500;
font-family: $header-sans-serif;
color: $courseware-navigation-color;
&:hover, &:focus{
&:hover,
&:focus {
text-decoration: none;
color: $courseware-hover-color;
}
......@@ -594,25 +595,26 @@ header.global-new {
}
}
.nav-global {
@extend %default-header-nav;
}
.nav-courseware {
@extend .nav-global;
@extend %default-header-nav;
float: right;
div {
display: inline-block;
margin: 0 21px 0 0;
text-transform: uppercase;
letter-spacing: 0!important;
position: relative;
vertical-align: middle;
&:last-child {
margin-right: 0;
margin-right: 10px;
}
a {
&.nav-courseware-button {
padding: 5px 45px 5px 45px;
border: 3px solid $courseware-button-border-color;
border-radius: 5px;
margin-top: -22px;
......@@ -628,6 +630,182 @@ header.global-new {
}
}
}
&.rwd {
nav {
max-width: 1180px;
width: 100%;
}
.mobile-menu-button {
@extend %t-action1;
display: inline;
float: left;
text-decoration: none;
color: $m-gray;
margin-top: 9px;
&:hover,
&:active,
&:focus {
text-decoration: none;
}
}
.logo {
position: absolute;
width: 54px;
left: calc( 50% - 90px );
top: 20px;
img {
width: 54px;
}
}
.nav-global,
.nav-courseware {
a {
@extend %t-action3;
&.nav-courseware-button {
width: 86px;
text-align: center;
margin-top: -3px;
}
}
}
.nav-global,
.nav-courseware-01 {
display: none;
}
.nav-global {
position: absolute;
top: 73px;
left: calc( 50% - 160px );
z-index: 1000;
width: 320px;
background: $m-blue-d3;
&.show {
display: inline;
}
a {
color: white;
padding: 10px;
font-weight: 300;
&:hover,
&:focus {
background: $m-blue-d5;
color: white;
border-bottom: none;
}
}
li {
display: block;
border-bottom: 1px solid $m-blue-d5;
}
}
.nav-courseware {
display: inline;
div:last-child {
margin-right: 0;
}
}
@include media( 320px ) {
nav {
width: 320px;
}
}
@include media( $desktop ) {
nav {
width: 100%;
}
.mobile-menu-button {
display: none;
}
.logo {
position: relative;
width: auto;
top: inherit;
left: inherit;
margin-left: 10px;
img {
width: auto;
}
}
.nav-global {
display: inline;
position: relative;
z-index: auto;
width: auto;
top: auto;
left: auto;
background: inherit;
a {
color: $courseware-navigation-color;
padding: 3px 10px;
font-weight: 500;
&:hover,
&:focus {
background: inherit;
color: $courseware-hover-color;
}
}
li {
display: inline-block;
border-bottom: none;
}
}
.nav-courseware {
div:last-child {
margin-right: 10px;
}
}
.nav-courseware-01 {
display: inline-block;
}
.desktop-hide {
display: none!important;
}
}
@include media( $xl-desktop ) {
nav {
padding: 17px 10px;
}
.nav-global,
.nav-courseware {
a {
font-size: 18px;
}
}
.logo {
margin-left: 0;
}
}
}
}
.view-register header.global-new .cta-register {
......
// lms - views - verification flow
// ====================
@import '../base/grid-settings';
@import 'neat/neat'; // lib - Neat
// MISC: extends - button
%btn-verify-primary {
......@@ -12,7 +14,6 @@
.is-expandable {
.title-expand {
}
.expandable-icon {
......@@ -438,7 +439,6 @@
}
}
}
}
}
......@@ -989,7 +989,7 @@
@extend %t-weight4;
position: absolute;
top: -($baseline*1.25);
left: 45%;
left: calc( 50% - 46px );
padding: ($baseline/2) ($baseline*1.5);
background: white;
text-align: center;
......@@ -1129,22 +1129,35 @@
}
.content-supplementary {
width: flex-grid(12,12);
@include box-sizing(border-box);
@include outer-container;
@include span-columns(12);
.list-help {
@include clearfix();
.help-item {
width: flex-grid(4,12);
@include fill-parent;
float: left;
margin-right: flex-gutter();
margin-bottom: 25px;
&:last-child {
margin-right: 0;
margin: 0;
}
}
}
&.help-item-technical {
width: flex-grid(8,12);
@include media( 550px ) {
.list-help {
.help-item {
@include span-columns(4);
margin-bottom: 0;
&.help-item-technical {
@include span-columns(8);
}
}
}
}
......@@ -1154,6 +1167,10 @@
// VIEW: select a track
&.step-select-track {
.container {
min-width: 0;
max-width: 1200px;
}
.sts-track {
@extend %text-sr;
......@@ -1161,11 +1178,10 @@
.form-register-choose {
@include clearfix();
width: flex-grid(12,12);
margin: ($baseline*2) 0;
.deco-divider {
width: flex-grid(12,12);
@include fill-parent;
float: left;
}
}
......@@ -1175,7 +1191,7 @@
}
.register-choice {
width: flex-grid(12,12);
@include fill-parent;
margin: 0 flex-gutter() $baseline 0;
border-top: ($baseline/4) solid $m-gray-d4;
padding: $baseline ($baseline*1.5);
......@@ -1190,28 +1206,35 @@
vertical-align: middle;
}
.wrapper-copy {
width: flex-grid(8,8);
}
.list-actions {
width: flex-grid(8,8);
@include fill-parent;
text-align: right;
float: right;
margin: ($baseline/4) 0;
border-top: none;
clear: both;
}
.title {
@extend %t-title5;
@extend %t-weight5;
margin-bottom: ($baseline/2);
width: calc( 100% - 30px );
}
.copy {
@extend %t-copy-base;
}
.action-select input {
@extend %t-weight4;
padding: ($baseline/2) ($baseline*0.75);
.action-select {
@include fill-parent;
input {
@extend %t-weight4;
padding: ($baseline/2) ($baseline*0.75);
width: 100%;
white-space: normal;
}
}
}
......@@ -1226,15 +1249,9 @@
display: block;
width: ($baseline*2.9);
height: ($baseline*4.2);
background: transparent url('../images/honor-ribbon.png') no-repeat 0 0;
}
.wrapper-copy {
width: flex-grid(8,8);
}
.list-actions {
width: flex-grid(8,8);
margin: ($baseline) 0;
}
......@@ -1249,19 +1266,12 @@
.deco-ribbon {
position: absolute;
top: -($baseline*1.5);
top: -10px;
right: $baseline;
display: block;
width: ($baseline*3);
height: ($baseline*4);
background: transparent url('../images/vcert-ribbon-s.png') no-repeat 0 0;
}
.list-actions {
margin: ($baseline/4) 0;
border-top: none;
width: flex-grid(4,12);
float: right;
width: 45px;
height: 45px;
background: transparent url('../images/verified-ribbon.png') no-repeat 0 0;
}
.action-intro, .action-select {
......@@ -1270,15 +1280,11 @@
}
.action-intro {
@include fill-parent;
@extend %copy-detail;
width: flex-grid(3,8);
text-align: left;
}
.action-select {
width: initial;
}
.action-select input {
@extend %btn-verify-primary;
}
......@@ -1301,7 +1307,7 @@
}
.help-register {
width: flex-grid(4,12);
@include span-columns(4);
.title {
@extend %hd-lv4;
......@@ -1333,8 +1339,8 @@
.contribution-options {
@include clearfix();
@include fill-parent;
margin: 0;
width: flex-grid(8,12);
&:after{
clear: none;
......@@ -1342,6 +1348,7 @@
}
.field {
@include fill-parent;
float: left;
margin: 0 ($baseline/2) ($baseline/2) 0;
padding: ($baseline/2) ($baseline*0.75);
......@@ -1380,6 +1387,65 @@
}
}
}
@include media(min-width 550px max-width 768px) {
.contribution-options {
.field {
@include span-columns(6);
&:nth-of-type(even) {
margin-right: 0;
}
}
}
.register-choice {
.list-actions {
float: left;
width: auto;
}
.action-select {
width: initial;
input {
width: initial;
}
}
}
}
@include media( $desktop ) {
.contribution-options {
.field {
width: auto;
}
}
.register-choice {
.list-actions {
@include span-columns(4);
width: auto;
}
.action-select {
width: initial;
input {
width: initial;
}
}
}
}
@include media( $xl-desktop ) {
.register-choice {
.list-actions {
float: right;
clear: none;
}
}
}
}
// VIEW: requirements
......
......@@ -4,11 +4,6 @@
from courseware.courses import course_image_url, get_course_about_section
from django.conf import settings
from edxmako.shortcuts import marketing_link
if settings.FEATURES.get('ENABLE_SHOPPING_CART'):
cart_link = reverse('shoppingcart.views.show_cart')
else:
cart_link = ""
%>
<%namespace name='static' file='../static_content.html'/>
<%! from microsite_configuration import microsite %>
......@@ -42,7 +37,7 @@
event.preventDefault();
});
% if settings.FEATURES.get('ENABLE_SHOPPING_CART') and settings.FEATURES.get('ENABLE_PAID_COURSE_REGISTRATION'):
% if is_shopping_cart_enabled:
add_course_complete_handler = function(jqXHR, textStatus) {
if (jqXHR.status == 200) {
location.href = "${cart_link}";
......@@ -162,7 +157,7 @@
## so that they can register and become a real user that can enroll.
% elif not is_shib_course and not can_enroll:
<span class="register disabled">${_("Enrollment is Closed")}</span>
%elif settings.FEATURES.get('ENABLE_PAID_COURSE_REGISTRATION') and registration_price:
%elif is_shopping_cart_enabled and registration_price:
<%
if user.is_authenticated():
reg_href = "#"
......
......@@ -11,6 +11,9 @@
<head dir="${dir_rtl}">
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
% if responsive:
<meta name="viewport" content="width=device-width, initial-scale=1">
% endif
<%! from django.utils.translation import ugettext as _ %>
<%! from microsite_configuration import microsite %>
<%! from microsite_configuration import page_title_breadcrumbs %>
......
......@@ -53,11 +53,15 @@ site_status_msg = get_site_status_msg(course_id)
% if user.is_authenticated():
<div class="left nav-global authenticated">
<%block name="navigation_global_links_authenticated">
% if settings.FEATURES.get('COURSES_ARE_BROWSABLE'):
<div class="nav-global-01">
<a href="${marketing_link('COURSES')}">${_('Find Courses')}</a>
</div>
% endif
<li class="nav-global-01">
<a href="${marketing_link('HOW_IT_WORKS')}">${_("How it Works")}</a>
</li>
<li class="nav-global-02">
<a href="${marketing_link('COURSES')}">${_("Find Courses")}</a>
</li>
<li class="nav-global-03">
<a href="${marketing_link('SCHOOLS')}">${_("Schools & Partners")}</a>
</li>
</%block>
</div>
......
<%! from django.utils.translation import ugettext as _ %>
<%namespace name='static' file='../static_content.html'/>
<header class="page-header">
<h2 class="title">
<span class="wrapper-sts">
......@@ -66,3 +68,7 @@
% endif
</h2>
</header>
<%block name="js_extra">
<%static:js group='rwd_header_footer'/>
</%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