From 3b928e3e99113af1c717b28b9d14f43e0a8b6f0e Mon Sep 17 00:00:00 2001 From: Michael LoTurco <mikeloturco13@gmail.com> Date: Wed, 28 Feb 2018 14:24:06 -0500 Subject: [PATCH] Adds EntitlementSupportView, Template, and base react element --- .../static/support/jsx/entitlements/index.jsx | 9 +++ lms/djangoapps/support/tests/test_views.py | 79 ------------------- lms/djangoapps/support/urls.py | 12 +-- .../support/views/course_entitlements.py | 25 +++++- lms/templates/support/entitlement.html | 34 ++++++++ package-lock.json | 13 ++- package.json | 4 +- webpack.common.config.js | 1 + 8 files changed, 78 insertions(+), 99 deletions(-) create mode 100644 lms/djangoapps/support/static/support/jsx/entitlements/index.jsx create mode 100644 lms/templates/support/entitlement.html diff --git a/lms/djangoapps/support/static/support/jsx/entitlements/index.jsx b/lms/djangoapps/support/static/support/jsx/entitlements/index.jsx new file mode 100644 index 00000000000..ce5b7487ccb --- /dev/null +++ b/lms/djangoapps/support/static/support/jsx/entitlements/index.jsx @@ -0,0 +1,9 @@ +import React from 'react'; + +const EntitlementSupportPage = () => ( + <div> + Base Entitlement Support Page + </div> +); + +export default EntitlementSupportPage; diff --git a/lms/djangoapps/support/tests/test_views.py b/lms/djangoapps/support/tests/test_views.py index 32b8489ad95..9e853c90814 100644 --- a/lms/djangoapps/support/tests/test_views.py +++ b/lms/djangoapps/support/tests/test_views.py @@ -437,82 +437,3 @@ class SupportViewEnrollmentsTests(SharedModuleStoreTestCase, SupportViewTestCase ) verified_mode.expiration_datetime = datetime(year=1970, month=1, day=9, tzinfo=UTC) verified_mode.save() - - -@ddt.ddt -class SupportViewCourseEntitlementsTests(SupportViewTestCase): - """ Tests for the course entitlement support view.""" - - def setUp(self): - super(SupportViewCourseEntitlementsTests, self).setUp() - self.user = UserFactory(is_staff=True) - SupportStaffRole().add_users(self.user) - self.client.login(username=self.user.username, password=TEST_PASSWORD) - - self.student = UserFactory.create(username='student', email='test@example.com', password='test') - self.course_uuid = uuid4() - - self.url = reverse('support:course_entitlement') - - @ddt.data('username', 'email') - def test_get_entitlements(self, search_string_type): - CourseEntitlementFactory.create(mode=CourseMode.VERIFIED, user=self.student, course_uuid=self.course_uuid) - url = self.url + getattr(self.student, search_string_type) - response = self.client.get(url) - self.assertEqual(response.status_code, 200) - data = json.loads(response.content) - self.assertEqual(len(data), 1) - self.assertDictContainsSubset({ - 'user': self.student.username, - 'course_uuid': unicode(self.course_uuid), - 'enrollment_course_run': None, - 'mode': CourseMode.VERIFIED, - 'support_details': [] - }, data[0]) - - def test_reinstate_entitlement(self): - selected_run = CourseEnrollmentFactory(mode=CourseMode.VERIFIED, user=self.student) - expired_entitlement = CourseEntitlementFactory.create( - mode=CourseMode.VERIFIED, user=self.student, enrollment_course_run=selected_run, expired_at=datetime.now() - ) - url = self.url + self.student.username - response = self.client.put(url, data=json.dumps( - { - 'entitlement_uuid': unicode(expired_entitlement.uuid), - 'reason': CourseEntitlementSupportDetail.LEAVE_SESSION - }), - content_type='application/json' - ) - self.assertEqual(response.status_code, 200) - data = json.loads(response.content) - self.assertEqual(len(data['support_details']), 1) - self.assertDictContainsSubset({ - 'support_user': self.user.username, - 'reason': CourseEntitlementSupportDetail.LEAVE_SESSION, - 'comments': None, - 'unenrolled_run': unicode(selected_run.course_id) - }, data['support_details'][0]) - - def test_create_entitlement(self): - CourseEntitlementFactory.create( - mode=CourseMode.VERIFIED, user=self.student, course_uuid=self.course_uuid, expired_at=datetime.now() - ) - url = self.url + self.student.username - response = self.client.post( - url, - data=json.dumps({ - 'course_uuid': unicode(self.course_uuid), - 'reason': CourseEntitlementSupportDetail.LEARNER_REQUEST_NEW, - 'mode': CourseMode.VERIFIED - }), - content_type='application/json', - ) - self.assertEqual(response.status_code, 201) - data = json.loads(response.content) - self.assertEqual(len(data['support_details']), 1) - self.assertDictContainsSubset({ - 'support_user': self.user.username, - 'reason': CourseEntitlementSupportDetail.LEARNER_REQUEST_NEW, - 'comments': None, - 'unenrolled_run': None - }, data['support_details'][0]) diff --git a/lms/djangoapps/support/urls.py b/lms/djangoapps/support/urls.py index 1bd324d23a6..494bfcb1a4c 100644 --- a/lms/djangoapps/support/urls.py +++ b/lms/djangoapps/support/urls.py @@ -11,21 +11,13 @@ from support.views.index import index from support.views.manage_user import ManageUserDetailView, ManageUserSupportView from support.views.refund import RefundSupportView -COURSE_ENTITLEMENTS_VIEW = EntitlementSupportView.as_view({ - 'get': 'list', - 'post': 'create', - 'put': 'update' -}) +COURSE_ENTITLEMENTS_VIEW = EntitlementSupportView.as_view() urlpatterns = [ url(r'^$', index, name="index"), url(r'^certificates/?$', CertificatesSupportView.as_view(), name="certificates"), url(r'^refund/?$', RefundSupportView.as_view(), name="refund"), - url( - r'^course_entitlement/(?P<username_or_email>[\w.@+-]+)?$', - COURSE_ENTITLEMENTS_VIEW, - name="course_entitlement" - ), + url(r'^course_entitlement/?$', COURSE_ENTITLEMENTS_VIEW, name="course_entitlement"), url(r'^enrollment/?$', EnrollmentSupportView.as_view(), name="enrollment"), url(r'^contact_us/?$', ContactUsView.as_view(), name="contact_us"), url( diff --git a/lms/djangoapps/support/views/course_entitlements.py b/lms/djangoapps/support/views/course_entitlements.py index 79d64fdced2..6a93c964641 100644 --- a/lms/djangoapps/support/views/course_entitlements.py +++ b/lms/djangoapps/support/views/course_entitlements.py @@ -6,20 +6,43 @@ from django.db import DatabaseError, transaction from django.db.models import Q from django.http import HttpResponseBadRequest from django.utils.decorators import method_decorator +from django.views.generic import View from edx_rest_framework_extensions.authentication import JwtAuthentication from rest_framework import permissions, status, viewsets from rest_framework.response import Response +from edxmako.shortcuts import render_to_response from entitlements.api.v1.permissions import IsAdminOrAuthenticatedReadOnly from entitlements.api.v1.serializers import SupportCourseEntitlementSerializer from entitlements.models import CourseEntitlement, CourseEntitlementSupportDetail +from lms.djangoapps.commerce.utils import EcommerceService from lms.djangoapps.support.decorators import require_support_permission from openedx.core.djangoapps.cors_csrf.authentication import SessionAuthenticationCrossDomainCsrf REQUIRED_CREATION_FIELDS = ['course_uuid', 'reason', 'mode'] -class EntitlementSupportView(viewsets.ModelViewSet): +class EntitlementSupportView(View): + """ + View for viewing and changing learner enrollments, used by the + support team. + """ + @method_decorator(require_support_permission) + def get(self, request): + """Render the enrollment support tool view.""" + support_actions = CourseEntitlementSupportDetail.get_support_actions_list() + + ecommerce_url = EcommerceService().get_order_dashboard_url() + context = { + 'username': request.GET.get('user', ''), + 'uses_bootstrap': True, + 'ecommerce_url': ecommerce_url, + 'support_actions': support_actions + } + return render_to_response('support/entitlement.html', context) + + +class EntitlementSupportListView(viewsets.ModelViewSet): """ Allows viewing and changing learner course entitlements, used the support team. """ diff --git a/lms/templates/support/entitlement.html b/lms/templates/support/entitlement.html new file mode 100644 index 00000000000..f193160f4c0 --- /dev/null +++ b/lms/templates/support/entitlement.html @@ -0,0 +1,34 @@ +<%page expression_filter="h"/> + +<%! +from django.utils.translation import ugettext as _ +from openedx.core.djangolib.js_utils import js_escaped_string +%> + +## Override the default styles_version to use Bootstrap +<%! main_css = "css/bootstrap/lms-main.css" %> + +<%namespace name='static' file='../static_content.html'/> + +<%inherit file="../main.html" /> + +<%block name="js_extra"> +</%block> + +<%block name="pagetitle"> + ${_("Entitlements")} +</%block> + +<%block name="content"> + <section class="container outside-app"> + ${static.renderReact( + component="EntitlementSupportPage", + id="entitlement-support-page", + props={ + 'ecommerceUrl': ecommerce_url, + 'supportReasons': support_actions + } + ) + } + </section> +</%block> diff --git a/package-lock.json b/package-lock.json index 06fd3ea6672..43d52557cc6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7981,11 +7981,10 @@ "integrity": "sha1-eHTi04kR0nGeoncx0yRF2l7EOUw=" }, "react": { - "version": "15.6.2", - "resolved": "https://registry.npmjs.org/react/-/react-15.6.2.tgz", - "integrity": "sha1-26BDSrQ5z+gvEI8PURZjkIF5qnI=", + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.1.0.tgz", + "integrity": "sha512-hvKYlKqde2JNnNiEzORvSA0J1L7uSZ43l+J89ZNoP4EXxQrVNH0CFj8vorfPou3w+1ou1BNMBir2VVsuXtETRA==", "requires": { - "create-react-class": "15.6.3", "fbjs": "0.8.16", "loose-envify": "1.3.1", "object-assign": "4.1.1", @@ -8003,9 +8002,9 @@ } }, "react-dom": { - "version": "15.6.2", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-15.6.2.tgz", - "integrity": "sha1-Qc+t9pO3V/rycIRDodH9WgK+9zA=", + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.1.0.tgz", + "integrity": "sha512-i9in5qW3H2PDinUPD9bnQK7tLAD8LhjYQ+fXi3nJOvVnxOO3ErHq6RNEnKY7pbjTPt155e74q7al8eBUuyLtew==", "requires": { "fbjs": "0.8.16", "loose-envify": "1.3.1", diff --git a/package.json b/package.json index c6d2a49b730..9030300d23b 100644 --- a/package.json +++ b/package.json @@ -40,8 +40,8 @@ "popper.js": "1.12.9", "prop-types": "15.6.0", "raw-loader": "0.5.1", - "react": "15.6.2", - "react-dom": "15.6.2", + "react": "16.1.0", + "react-dom": "16.1.0", "react-slick": "0.16.0", "requirejs": "2.3.5", "rtlcss": "2.2.1", diff --git a/webpack.common.config.js b/webpack.common.config.js index 31c0cc1bd83..2c71829b2cf 100644 --- a/webpack.common.config.js +++ b/webpack.common.config.js @@ -29,6 +29,7 @@ module.exports = { LearnerAnalyticsDashboard: './lms/static/js/learner_analytics_dashboard/LearnerAnalyticsDashboard.jsx', UpsellExperimentModal: './lms/static/common/js/components/UpsellExperimentModal.jsx', PortfolioExperimentUpsellModal: './lms/static/common/js/components/PortfolioExperimentUpsellModal.jsx', + EntitlementSupportPage: './lms/djangoapps/support/static/support/jsx/entitlements/index.jsx', // Learner Dashboard EntitlementFactory: './lms/static/js/learner_dashboard/course_entitlement_factory.js', -- GitLab