diff --git a/lms/djangoapps/shoppingcart/context_processor.py b/lms/djangoapps/shoppingcart/context_processor.py new file mode 100644 index 0000000000..b94f1cac2a --- /dev/null +++ b/lms/djangoapps/shoppingcart/context_processor.py @@ -0,0 +1,23 @@ +""" +This is the shoppingcart context_processor module. +Currently the only context_processor detects whether request.user has a cart that should be displayed in the +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 + + +def user_has_cart_context_processor(request): + """ + Checks if request has an authenticated user. If so, checks if request.user has a cart that should + be displayed. Anonymous users don't. + Adds `display_shopping_cart` to the context + """ + return {'display_shopping_cart': ( + request.user.is_authenticated() and # user is logged in and + settings.MITX_FEATURES.get('ENABLE_PAID_COURSE_REGISTRATION') and # settings enable paid course reg and + settings.MITX_FEATURES.get('ENABLE_SHOPPING_CART') and # settings enable shopping cart and + shoppingcart.models.Order.user_cart_has_items(request.user) # user's cart has items + )} diff --git a/lms/djangoapps/shoppingcart/tests/test_context_processor.py b/lms/djangoapps/shoppingcart/tests/test_context_processor.py new file mode 100644 index 0000000000..4cedca8d14 --- /dev/null +++ b/lms/djangoapps/shoppingcart/tests/test_context_processor.py @@ -0,0 +1,82 @@ +""" +Unit tests for shoppingcart context_processor +""" +from mock import patch, Mock +from django.conf import settings +from django.contrib.auth.models import AnonymousUser +from django.test.utils import override_settings + +from courseware.tests.tests import TEST_DATA_MONGO_MODULESTORE +from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase +from xmodule.modulestore.tests.factories import CourseFactory +from student.tests.factories import UserFactory +from course_modes.tests.factories import CourseModeFactory +from shoppingcart.models import Order, PaidCourseRegistration +from shoppingcart.context_processor import user_has_cart_context_processor + + +@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE) +class UserCartContextProcessorUnitTest(ModuleStoreTestCase): + """ + Unit test for shoppingcart context_processor + """ + def setUp(self): + self.user = UserFactory.create() + self.request = Mock() + + def add_to_cart(self): + """ + Adds content to self.user's cart + """ + course = CourseFactory.create(org='MITx', number='999', display_name='Robot Super Course') + CourseModeFactory(course_id=course.id) + cart = Order.get_cart_for_user(self.user) + PaidCourseRegistration.add_to_order(cart, course.id) + + @patch.dict(settings.MITX_FEATURES, {'ENABLE_SHOPPING_CART': False, 'ENABLE_PAID_COURSE_REGISTRATION': True}) + def test_no_enable_shoppingcart(self): + """ + Tests when MITX_FEATURES['ENABLE_SHOPPING_CART'] is not set + """ + self.add_to_cart() + self.request.user = self.user + context = user_has_cart_context_processor(self.request) + self.assertFalse(context['display_shopping_cart']) + + @patch.dict(settings.MITX_FEATURES, {'ENABLE_SHOPPING_CART': True, 'ENABLE_PAID_COURSE_REGISTRATION': False}) + def test_no_enable_paid_course_registration(self): + """ + Tests when MITX_FEATURES['ENABLE_PAID_COURSE_REGISTRATION'] is not set + """ + self.add_to_cart() + self.request.user = self.user + context = user_has_cart_context_processor(self.request) + self.assertFalse(context['display_shopping_cart']) + + @patch.dict(settings.MITX_FEATURES, {'ENABLE_SHOPPING_CART': True, 'ENABLE_PAID_COURSE_REGISTRATION': True}) + def test_anonymous_user(self): + """ + Tests when request.user is anonymous + """ + self.request.user = AnonymousUser() + context = user_has_cart_context_processor(self.request) + self.assertFalse(context['display_shopping_cart']) + + @patch.dict(settings.MITX_FEATURES, {'ENABLE_SHOPPING_CART': True, 'ENABLE_PAID_COURSE_REGISTRATION': True}) + def test_no_items_in_cart(self): + """ + Tests when request.user doesn't have a cart with items + """ + self.request.user = self.user + context = user_has_cart_context_processor(self.request) + self.assertFalse(context['display_shopping_cart']) + + @patch.dict(settings.MITX_FEATURES, {'ENABLE_SHOPPING_CART': True, 'ENABLE_PAID_COURSE_REGISTRATION': True}) + def test_items_in_cart(self): + """ + Tests when request.user has a cart with items + """ + self.add_to_cart() + self.request.user = self.user + context = user_has_cart_context_processor(self.request) + self.assertTrue(context['display_shopping_cart']) diff --git a/lms/envs/common.py b/lms/envs/common.py index db66b821ca..ae37bf6202 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -272,6 +272,9 @@ TEMPLATE_CONTEXT_PROCESSORS = ( # Hack to get required link URLs to password reset templates 'mitxmako.shortcuts.marketing_link_context_processor', + + # Shoppingcart processor (detects if request.user has a cart) + 'shoppingcart.context_processor.user_has_cart_context_processor', ) # use the ratelimit backend to prevent brute force attacks diff --git a/lms/templates/navigation.html b/lms/templates/navigation.html index e2e1d9d664..b1e4c091e0 100644 --- a/lms/templates/navigation.html +++ b/lms/templates/navigation.html @@ -9,8 +9,6 @@ from django.utils.translation import ugettext as _ import branding # app that handles site status messages from status.status import get_site_status_msg -# shopping cart -import shoppingcart %> ## Provide a hook for themes to inject branding on top. @@ -83,9 +81,7 @@ site_status_msg = get_site_status_msg(course_id) - % if settings.MITX_FEATURES.get('ENABLE_PAID_COURSE_REGISTRATION') and \ - settings.MITX_FEATURES.get('ENABLE_SHOPPING_CART') and \ - shoppingcart.models.Order.user_cart_has_items(user): + % if display_shopping_cart: # see shoppingcart.context_processor.user_has_cart_context_processor