diff --git a/common/static/js/src/accessibility_tools.js b/common/static/js/src/accessibility_tools.js
index ea6a4fcd31..3c98f56759 100644
--- a/common/static/js/src/accessibility_tools.js
+++ b/common/static/js/src/accessibility_tools.js
@@ -98,7 +98,7 @@ var accessible_modal = function(trigger, closeButtonId, modalId, mainPageId) {
});
// get modal to exit on escape key
- $('.modal').on('keydown', function(e) {
+ $(modalId).on('keydown', function(e) {
var keyCode = e.keyCode || e.which;
// 27 is the javascript keycode for the ESC key
if (keyCode === 27) {
diff --git a/lms/static/js/dashboard/dropdown.js b/lms/static/js/dashboard/dropdown.js
index 96b5e22328..c7099bee64 100644
--- a/lms/static/js/dashboard/dropdown.js
+++ b/lms/static/js/dashboard/dropdown.js
@@ -8,10 +8,10 @@ var edx = edx || {};
// Generate the properties object to be passed along with business intelligence events.
edx.dashboard.dropdown.toggleCourseActionsDropdownMenu = function(event) {
- // define variables for code legibility
- var dashboardIndex = $(event.currentTarget).data().dashboardIndex,
- $dropdown = $('#actions-dropdown-' + dashboardIndex),
- $dropdownButton = $('#actions-dropdown-link-' + dashboardIndex),
+ var $target = $(event.currentTarget),
+ dashboardIndex = $target.data().dashboardIndex,
+ $dropdown = $($target.data('dropdownSelector') || '#actions-dropdown-' + dashboardIndex),
+ $dropdownButton = $($target.data('dropdownButtonSelector') || '#actions-dropdown-link-' + dashboardIndex),
ariaExpandedState = ($dropdownButton.attr('aria-expanded') === 'true'),
menuItems = $dropdown.find('a');
@@ -76,14 +76,15 @@ var edx = edx || {};
});
};
- edx.dashboard.dropdown.bindToggleButtons = function() {
- $('.action-more').bind(
+ edx.dashboard.dropdown.bindToggleButtons = function(selector) {
+ $(selector).bind(
'click',
edx.dashboard.dropdown.toggleCourseActionsDropdownMenu
);
};
$(document).ready(function() {
- edx.dashboard.dropdown.bindToggleButtons();
+ edx.dashboard.dropdown.bindToggleButtons('.action-more');
+ edx.dashboard.dropdown.bindToggleButtons('.js-entitlement-action-more');
});
}(jQuery));
diff --git a/lms/static/js/learner_dashboard/entitlement_unenrollment_factory.js b/lms/static/js/learner_dashboard/entitlement_unenrollment_factory.js
new file mode 100644
index 0000000000..92828b90cf
--- /dev/null
+++ b/lms/static/js/learner_dashboard/entitlement_unenrollment_factory.js
@@ -0,0 +1,12 @@
+(function(define) {
+ 'use strict';
+
+ define([
+ 'js/learner_dashboard/views/entitlement_unenrollment_view'
+ ],
+ function(EntitlementUnenrollmentView) {
+ return function(options) {
+ return new EntitlementUnenrollmentView(options);
+ };
+ });
+}).call(this, define || RequireJS.define);
diff --git a/lms/static/js/learner_dashboard/views/entitlement_unenrollment_view.js b/lms/static/js/learner_dashboard/views/entitlement_unenrollment_view.js
new file mode 100644
index 0000000000..00387be204
--- /dev/null
+++ b/lms/static/js/learner_dashboard/views/entitlement_unenrollment_view.js
@@ -0,0 +1,134 @@
+(function(define) {
+ 'use strict';
+ define(['backbone',
+ 'jquery',
+ 'gettext',
+ 'edx-ui-toolkit/js/utils/html-utils'
+ ],
+ function(Backbone, $, gettext, HtmlUtils) {
+ return Backbone.View.extend({
+ el: '.js-entitlement-unenrollment-modal',
+ closeButtonSelector: '.js-entitlement-unenrollment-modal .js-entitlement-unenrollment-modal-close-btn',
+ headerTextSelector: '.js-entitlement-unenrollment-modal .js-entitlement-unenrollment-modal-header-text',
+ errorTextSelector: '.js-entitlement-unenrollment-modal .js-entitlement-unenrollment-modal-error-text',
+ submitButtonSelector: '.js-entitlement-unenrollment-modal .js-entitlement-unenrollment-modal-submit',
+ triggerSelector: '.js-entitlement-action-unenroll',
+ mainPageSelector: '#dashboard-main',
+ genericErrorMsg: gettext('Your unenrollment request could not be processed. Please try again later.'),
+
+ initialize: function(options) {
+ var view = this;
+ this.dashboardPath = options.dashboardPath;
+ this.signInPath = options.signInPath;
+
+ this.$submitButton = $(this.submitButtonSelector);
+ this.$headerText = $(this.headerTextSelector);
+ this.$errorText = $(this.errorTextSelector);
+
+ this.$submitButton.on('click', this.handleSubmit.bind(this));
+
+ $(this.triggerSelector).each(function() {
+ var $trigger = $(this);
+
+ $trigger.on('click', view.handleTrigger.bind(view));
+
+ if (window.accessible_modal) {
+ window.accessible_modal(
+ '#' + $trigger.attr('id'),
+ view.closeButtonSelector,
+ '#' + view.$el.attr('id'),
+ view.mainPageSelector
+ );
+ }
+ });
+ },
+
+ handleTrigger: function(event) {
+ var $trigger = $(event.target),
+ courseName = $trigger.data('courseName'),
+ courseNumber = $trigger.data('courseNumber'),
+ apiEndpoint = $trigger.data('entitlementApiEndpoint');
+
+ this.resetModal();
+ this.setHeaderText(courseName, courseNumber);
+ this.setSubmitData(apiEndpoint);
+
+ if (window.edx && window.edx.dashboard && window.edx.dashboard.dropdown) {
+ window.edx.dashboard.dropdown.toggleCourseActionsDropdownMenu(event);
+ this.$el.css('position', 'fixed');
+ }
+ },
+
+ handleSubmit: function() {
+ var apiEndpoint = this.$submitButton.data('entitlementApiEndpoint');
+
+ if (apiEndpoint === undefined) {
+ this.setError(this.genericErrorMsg);
+ return;
+ }
+
+ this.$submitButton.prop('disabled', true);
+ $.ajax({
+ url: apiEndpoint,
+ method: 'DELETE',
+ complete: this.onComplete.bind(this)
+ });
+ },
+
+ resetModal: function() {
+ this.$submitButton.removeData();
+ this.$submitButton.prop('disabled', false);
+ this.$headerText.empty();
+ this.$errorText.removeClass('entitlement-unenrollment-modal-error-text-visible');
+ this.$errorText.empty();
+ },
+
+ setError: function(message) {
+ this.$submitButton.prop('disabled', true);
+ this.$errorText.empty();
+ HtmlUtils.setHtml(
+ this.$errorText,
+ message
+ );
+ this.$errorText.addClass('entitlement-unenrollment-modal-error-text-visible');
+ },
+
+ setHeaderText: function(courseName, courseNumber) {
+ this.$headerText.empty();
+ HtmlUtils.setHtml(
+ this.$headerText,
+ HtmlUtils.interpolateHtml(
+ gettext('Are you sure you want to unenroll from {courseName} ({courseNumber})? You will be refunded the amount you paid.'), // eslint-disable-line max-len
+ {
+ courseName: courseName,
+ courseNumber: courseNumber
+ }
+ )
+ );
+ },
+
+ setSubmitData: function(apiEndpoint) {
+ this.$submitButton.removeData();
+ this.$submitButton.data('entitlementApiEndpoint', apiEndpoint);
+ },
+
+ onComplete: function(xhr) {
+ var status = xhr.status,
+ message = xhr.responseJSON && xhr.responseJSON.detail;
+
+ if (status === 204) {
+ this.redirectTo(this.dashboardPath);
+ } else if (status === 401 && message === 'Authentication credentials were not provided.') {
+ this.redirectTo(this.signInPath + '?next=' + encodeURIComponent(this.dashboardPath));
+ } else {
+ this.setError(this.genericErrorMsg);
+ }
+ },
+
+ redirectTo: function(path) {
+ window.location.href = path;
+ }
+ });
+ }
+ );
+}).call(this, define || RequireJS.define);
diff --git a/lms/static/js/spec/dashboard/dropdown_spec.js b/lms/static/js/spec/dashboard/dropdown_spec.js
index 1eeaf388b4..96f7418db3 100644
--- a/lms/static/js/spec/dashboard/dropdown_spec.js
+++ b/lms/static/js/spec/dashboard/dropdown_spec.js
@@ -31,7 +31,7 @@ define(['js/dashboard/dropdown', 'jquery.simulate'],
describe('edx.dashboard.dropdown.toggleCourseActionsDropdownMenu', function() {
beforeEach(function() {
loadFixtures('js/fixtures/dashboard/dashboard.html');
- window.edx.dashboard.dropdown.bindToggleButtons();
+ window.edx.dashboard.dropdown.bindToggleButtons('.action-more');
});
it('Clicking the .action-more button toggles the menu', function() {
diff --git a/lms/static/js/spec/learner_dashboard/entitlement_unenrollment_view_spec.js b/lms/static/js/spec/learner_dashboard/entitlement_unenrollment_view_spec.js
new file mode 100644
index 0000000000..672d2e543b
--- /dev/null
+++ b/lms/static/js/spec/learner_dashboard/entitlement_unenrollment_view_spec.js
@@ -0,0 +1,193 @@
+define([
+ 'backbone',
+ 'jquery',
+ 'js/learner_dashboard/views/entitlement_unenrollment_view'
+], function(Backbone, $, EntitlementUnenrollmentView) {
+ 'use strict';
+
+ describe('EntitlementUnenrollmentView', function() {
+ var view = null,
+ options = {
+ dashboardPath: '/dashboard',
+ signInPath: '/login'
+ },
+
+ initView = function() {
+ return new EntitlementUnenrollmentView(options);
+ },
+
+ modalHtml = 'Unenroll ' +
+ 'Unenroll ' +
+ '
' +
+ ' ' +
+ ' ' +
+ ' ' +
+ '
';
+
+ beforeEach(function() {
+ setFixtures(modalHtml);
+ view = initView();
+ });
+
+ afterEach(function() {
+ view.remove();
+ });
+
+ describe('when an unenroll link is clicked', function() {
+ it('should reset the modal and set the correct values for header/submit', function() {
+ var $link1 = $('#link1'),
+ $link2 = $('#link2'),
+ $headerTxt = $('.js-entitlement-unenrollment-modal-header-text'),
+ $errorTxt = $('.js-entitlement-unenrollment-modal-error-text'),
+ $submitBtn = $('.js-entitlement-unenrollment-modal-submit');
+
+ $link1.trigger('click');
+ expect($headerTxt.html().startsWith('Are you sure you want to unenroll from Test Course 1')).toBe(true);
+ expect($submitBtn.data()).toEqual({entitlementApiEndpoint: '/test/api/endpoint/1'});
+ expect($submitBtn.prop('disabled')).toBe(false);
+ expect($errorTxt.html()).toEqual('');
+ expect($errorTxt.hasClass('entitlement-unenrollment-modal-error-text-visible')).toBe(false);
+
+ // Set an error so that we can see that the modal is reset properly when clicked again
+ view.setError('This is an error');
+ expect($errorTxt.html()).toEqual('This is an error');
+ expect($errorTxt.hasClass('entitlement-unenrollment-modal-error-text-visible')).toBe(true);
+ expect($submitBtn.prop('disabled')).toBe(true);
+
+ $link2.trigger('click');
+ expect($headerTxt.html().startsWith('Are you sure you want to unenroll from Test Course 2')).toBe(true);
+ expect($submitBtn.data()).toEqual({entitlementApiEndpoint: '/test/api/endpoint/2'});
+ expect($submitBtn.prop('disabled')).toBe(false);
+ expect($errorTxt.html()).toEqual('');
+ expect($errorTxt.hasClass('entitlement-unenrollment-modal-error-text-visible')).toBe(false);
+ });
+ });
+
+ describe('when the unenroll submit button is clicked', function() {
+ it('should send a DELETE request to the configured apiEndpoint', function() {
+ var $submitBtn = $('.js-entitlement-unenrollment-modal-submit'),
+ apiEndpoint = '/test/api/endpoint/1';
+
+ view.setSubmitData(apiEndpoint);
+
+ spyOn($, 'ajax').and.callFake(function(opts) {
+ expect(opts.url).toEqual(apiEndpoint);
+ expect(opts.method).toEqual('DELETE');
+ expect(opts.complete).toBeTruthy();
+ });
+
+ $submitBtn.trigger('click');
+ expect($.ajax).toHaveBeenCalled();
+ });
+
+ it('should set an error and disable submit if the apiEndpoint has not been properly set', function() {
+ var $errorTxt = $('.js-entitlement-unenrollment-modal-error-text'),
+ $submitBtn = $('.js-entitlement-unenrollment-modal-submit');
+
+ expect($submitBtn.data()).toEqual({});
+ expect($submitBtn.prop('disabled')).toBe(false);
+ expect($errorTxt.html()).toEqual('');
+ expect($errorTxt.hasClass('entitlement-unenrollment-modal-error-text-visible')).toBe(false);
+
+ spyOn($, 'ajax');
+ $submitBtn.trigger('click');
+ expect($.ajax).not.toHaveBeenCalled();
+
+ expect($submitBtn.data()).toEqual({});
+ expect($submitBtn.prop('disabled')).toBe(true);
+ expect($errorTxt.html()).toEqual(view.genericErrorMsg);
+ expect($errorTxt.hasClass('entitlement-unenrollment-modal-error-text-visible')).toBe(true);
+ });
+
+ describe('when the unenroll request is complete', function() {
+ it('should redirect to the dashboard if the request was successful', function() {
+ var $submitBtn = $('.js-entitlement-unenrollment-modal-submit'),
+ apiEndpoint = '/test/api/endpoint/1';
+
+ view.setSubmitData(apiEndpoint);
+
+ spyOn($, 'ajax').and.callFake(function(opts) {
+ expect(opts.url).toEqual(apiEndpoint);
+ expect(opts.method).toEqual('DELETE');
+ expect(opts.complete).toBeTruthy();
+
+ opts.complete({
+ status: 204,
+ responseJSON: {detail: 'success'}
+ });
+ });
+ spyOn(view, 'redirectTo');
+
+ $submitBtn.trigger('click');
+ expect($.ajax).toHaveBeenCalled();
+ expect(view.redirectTo).toHaveBeenCalledWith(view.dashboardPath);
+ });
+
+ it('should redirect to the login page if the request failed with an auth error', function() {
+ var $submitBtn = $('.js-entitlement-unenrollment-modal-submit'),
+ apiEndpoint = '/test/api/endpoint/1';
+
+ view.setSubmitData(apiEndpoint);
+
+ spyOn($, 'ajax').and.callFake(function(opts) {
+ expect(opts.url).toEqual(apiEndpoint);
+ expect(opts.method).toEqual('DELETE');
+ expect(opts.complete).toBeTruthy();
+
+ opts.complete({
+ status: 401,
+ responseJSON: {detail: 'Authentication credentials were not provided.'}
+ });
+ });
+ spyOn(view, 'redirectTo');
+
+ $submitBtn.trigger('click');
+ expect($.ajax).toHaveBeenCalled();
+ expect(view.redirectTo).toHaveBeenCalledWith(
+ view.signInPath + '?next=' + encodeURIComponent(view.dashboardPath)
+ );
+ });
+
+ it('should set an error and disable submit if a non-auth error occurs', function() {
+ var $errorTxt = $('.js-entitlement-unenrollment-modal-error-text'),
+ $submitBtn = $('.js-entitlement-unenrollment-modal-submit'),
+ apiEndpoint = '/test/api/endpoint/1';
+
+ view.setSubmitData(apiEndpoint);
+
+ spyOn($, 'ajax').and.callFake(function(opts) {
+ expect(opts.url).toEqual(apiEndpoint);
+ expect(opts.method).toEqual('DELETE');
+ expect(opts.complete).toBeTruthy();
+
+ opts.complete({
+ status: 400,
+ responseJSON: {detail: 'Bad request.'}
+ });
+ });
+ spyOn(view, 'redirectTo');
+
+ expect($submitBtn.prop('disabled')).toBe(false);
+ expect($errorTxt.html()).toEqual('');
+ expect($errorTxt.hasClass('entitlement-unenrollment-modal-error-text-visible')).toBe(false);
+
+ $submitBtn.trigger('click');
+
+ expect($submitBtn.prop('disabled')).toBe(true);
+ expect($errorTxt.html()).toEqual(view.genericErrorMsg);
+ expect($errorTxt.hasClass('entitlement-unenrollment-modal-error-text-visible')).toBe(true);
+
+ expect($.ajax).toHaveBeenCalled();
+ expect(view.redirectTo).not.toHaveBeenCalled();
+ });
+ });
+ });
+ });
+}
+);
diff --git a/lms/static/js/toggle_login_modal.js b/lms/static/js/toggle_login_modal.js
index f9dbf5d9a7..bb38c52639 100644
--- a/lms/static/js/toggle_login_modal.js
+++ b/lms/static/js/toggle_login_modal.js
@@ -29,7 +29,7 @@
var o = options;
$(this).click(function(e) {
- $('.modal').hide();
+ $('.modal, .js-modal').hide();
var modal_id = $(this).attr('href');
@@ -113,8 +113,12 @@
$(document).ready(function($) {
$('a[rel*=leanModal]').each(function() {
- $(this).leanModal({top: 120, overlay: 1, closeButton: '.close-modal', position: 'absolute'});
- embed = $($(this).attr('href')).find('iframe');
+ var $link = $(this),
+ closeButton = $link.data('modalCloseButtonSelector') || '.close-modal',
+ embed;
+
+ $link.leanModal({top: 120, overlay: 1, closeButton: closeButton, position: 'absolute'});
+ embed = $($link.attr('href')).find('iframe');
if (embed.length > 0 && embed.attr('src')) {
var sep = (embed.attr('src').indexOf('?') > 0) ? '&' : '?';
embed.data('src', embed.attr('src') + sep + 'autoplay=1&rel=0');
diff --git a/lms/static/lms/js/build.js b/lms/static/lms/js/build.js
index 21eb317bd2..b994a38848 100644
--- a/lms/static/lms/js/build.js
+++ b/lms/static/lms/js/build.js
@@ -34,6 +34,7 @@
'js/header_factory',
'js/learner_dashboard/course_entitlement_factory',
'js/learner_dashboard/unenrollment_factory',
+ 'js/learner_dashboard/entitlement_unenrollment_factory',
'js/learner_dashboard/program_details_factory',
'js/learner_dashboard/program_list_factory',
'js/student_account/logistration_factory',
diff --git a/lms/static/lms/js/spec/main.js b/lms/static/lms/js/spec/main.js
index 278531ee91..d81b26dae4 100644
--- a/lms/static/lms/js/spec/main.js
+++ b/lms/static/lms/js/spec/main.js
@@ -764,6 +764,7 @@
'js/spec/learner_dashboard/program_details_view_spec.js',
'js/spec/learner_dashboard/program_details_sidebar_view_spec.js',
'js/spec/learner_dashboard/unenroll_view_spec.js',
+ 'js/spec/learner_dashboard/entitlement_unenrollment_view_spec.js',
'js/spec/learner_dashboard/course_card_view_spec.js',
'js/spec/learner_dashboard/course_enroll_view_spec.js',
'js/spec/learner_dashboard/course_entitlement_view_spec.js',
diff --git a/lms/static/sass/_build-lms-v1.scss b/lms/static/sass/_build-lms-v1.scss
index a6afb4dbf4..3081a4b704 100644
--- a/lms/static/sass/_build-lms-v1.scss
+++ b/lms/static/sass/_build-lms-v1.scss
@@ -39,6 +39,7 @@
// shared - platform
@import 'multicourse/home';
@import 'multicourse/dashboard';
+@import 'multicourse/entitlement_dashboard';
@import 'multicourse/account';
@import 'multicourse/courses';
@import 'multicourse/course_about';
diff --git a/lms/static/sass/multicourse/_dashboard.scss b/lms/static/sass/multicourse/_dashboard.scss
index 1af3598aed..d66357ff5e 100644
--- a/lms/static/sass/multicourse/_dashboard.scss
+++ b/lms/static/sass/multicourse/_dashboard.scss
@@ -1534,6 +1534,12 @@ a.fade-cover {
#unenroll-modal {
margin-top: -60px;
+
+ .modal-form-error {
+ background: tint($red, 95%);
+ margin-left: $baseline;
+ margin-right: $baseline;
+ }
}
.reasons_survey {
diff --git a/lms/static/sass/multicourse/_entitlement_dashboard.scss b/lms/static/sass/multicourse/_entitlement_dashboard.scss
new file mode 100644
index 0000000000..bc6a89f78c
--- /dev/null
+++ b/lms/static/sass/multicourse/_entitlement_dashboard.scss
@@ -0,0 +1,69 @@
+.entitlement-actions-wrapper {
+ @extend .wrapper-action-more;
+
+ .entitlement-action {
+ @extend .action;
+ }
+
+ .entitlement-action-more {
+ @extend .action-more;
+ }
+
+ .entitlement-actions-dropdown {
+ @extend .actions-dropdown;
+
+ .entitlement-actions-dropdown-list {
+ @extend .actions-dropdown-list;
+
+ .entitlement-actions-item {
+ @extend .actions-item;
+ }
+ }
+ }
+}
+
+.entitlement-unenrollment-modal {
+ @extend .modal;
+
+ margin-top: -3*$baseline;
+
+ .entitlement-unenrollment-modal-inner-wrapper {
+ @extend .inner-wrapper;
+
+ .entitlement-unenrollment-modal-close-btn {
+ @extend .close-modal;
+ }
+
+ .entitlement-unenrollment-modal-header {
+ @extend .unenroll-header;
+ }
+
+ .entitlement-unenrollment-modal-error-text {
+ @extend .modal-form-error;
+
+ background: tint($red, 95%);
+ margin-left: $baseline;
+ margin-right: $baseline;
+ }
+
+ .entitlement-unenrollment-modal-error-text-visible {
+ display: block;
+ }
+
+ .entitlement-unenrollment-modal-submit-wrapper {
+ margin-bottom: $baseline*0.6;
+ position: relative;
+ z-index: 2;
+ padding: $baseline ($baseline*2);
+
+ .entitlement-unenrollment-modal-submit {
+ display: block;
+ height: auto;
+ margin: 0 auto;
+ width: 100%;
+ white-space: normal;
+ }
+ }
+ }
+}
+
diff --git a/lms/templates/dashboard.html b/lms/templates/dashboard.html
index 1c9a51f756..a9d179f5e8 100644
--- a/lms/templates/dashboard.html
+++ b/lms/templates/dashboard.html
@@ -60,6 +60,12 @@ from student.models import CourseEnrollment
isEdx: false
});
%static:require_module>
+ <%static:require_module module_name="js/learner_dashboard/entitlement_unenrollment_factory" class_name="EntitlementUnenrollmentFactory">
+ EntitlementUnenrollmentFactory({
+ dashboardPath: "${reverse('dashboard') | n, js_escaped_string}",
+ signInPath: "${reverse('signin_user') | n, js_escaped_string}"
+ });
+ %static:require_module>
% if settings.FEATURES.get('ENABLE_DASHBOARD_SEARCH'):
<%static:require_module module_name="course_search/js/dashboard_search_factory" class_name="DashboardSearchFactory">
DashboardSearchFactory();
@@ -161,6 +167,7 @@ from student.models import CourseEnrollment
session_id = enrollment.course_id
show_courseware_link = (session_id in show_courseware_links_for)
cert_status = cert_statuses.get(session_id)
+ can_refund_entitlement = entitlement and entitlement.is_entitlement_refundable()
can_unenroll = (not cert_status) or cert_status.get('can_unenroll') if not unfulfilled_entitlement else False
credit_status = credit_statuses.get(session_id)
show_email_settings = (session_id in show_email_settings_for)
@@ -173,7 +180,7 @@ from student.models import CourseEnrollment
show_consent_link = (session_id in consent_required_courses)
course_overview = enrollment.course_overview
%>
- <%include file='dashboard/_dashboard_course_listing.html' args='course_overview=course_overview, course_card_index=dashboard_index, enrollment=enrollment, is_unfulfilled_entitlement=is_unfulfilled_entitlement, is_fulfilled_entitlement=is_fulfilled_entitlement, entitlement=entitlement, entitlement_session=entitlement_session, entitlement_available_sessions=entitlement_available_sessions, entitlement_expiration_date=entitlement_expiration_date, entitlement_expired_at=entitlement_expired_at, show_courseware_link=show_courseware_link, cert_status=cert_status, can_unenroll=can_unenroll, credit_status=credit_status, show_email_settings=show_email_settings, course_mode_info=course_mode_info, is_paid_course=is_paid_course, is_course_blocked=is_course_blocked, verification_status=course_verification_status, course_requirements=course_requirements, dashboard_index=dashboard_index, share_settings=share_settings, user=user, related_programs=related_programs, display_course_modes_on_dashboard=display_course_modes_on_dashboard, show_consent_link=show_consent_link, enterprise_customer_name=enterprise_customer_name' />
+ <%include file='dashboard/_dashboard_course_listing.html' args='course_overview=course_overview, course_card_index=dashboard_index, enrollment=enrollment, is_unfulfilled_entitlement=is_unfulfilled_entitlement, is_fulfilled_entitlement=is_fulfilled_entitlement, entitlement=entitlement, entitlement_session=entitlement_session, entitlement_available_sessions=entitlement_available_sessions, entitlement_expiration_date=entitlement_expiration_date, entitlement_expired_at=entitlement_expired_at, show_courseware_link=show_courseware_link, cert_status=cert_status, can_refund_entitlement=can_refund_entitlement, can_unenroll=can_unenroll, credit_status=credit_status, show_email_settings=show_email_settings, course_mode_info=course_mode_info, is_paid_course=is_paid_course, is_course_blocked=is_course_blocked, verification_status=course_verification_status, course_requirements=course_requirements, dashboard_index=dashboard_index, share_settings=share_settings, user=user, related_programs=related_programs, display_course_modes_on_dashboard=display_course_modes_on_dashboard, show_consent_link=show_consent_link, enterprise_customer_name=enterprise_customer_name' />
% endfor
@@ -322,3 +329,5 @@ from student.models import CourseEnrollment
+
+<%include file="dashboard/_dashboard_entitlement_unenrollment_modal.html"/>
diff --git a/lms/templates/dashboard/_dashboard_course_listing.html b/lms/templates/dashboard/_dashboard_course_listing.html
index f6ee89513e..9d5f52fae5 100644
--- a/lms/templates/dashboard/_dashboard_course_listing.html
+++ b/lms/templates/dashboard/_dashboard_course_listing.html
@@ -1,4 +1,4 @@
-<%page args="course_overview, enrollment, entitlement, entitlement_session, course_card_index, is_unfulfilled_entitlement, is_fulfilled_entitlement, entitlement_available_sessions, entitlement_expiration_date, entitlement_expired_at, show_courseware_link, cert_status, can_unenroll, credit_status, show_email_settings, course_mode_info, is_paid_course, is_course_blocked, verification_status, course_requirements, dashboard_index, share_settings, related_programs, display_course_modes_on_dashboard, show_consent_link, enterprise_customer_name" expression_filter="h"/>
+<%page args="course_overview, enrollment, entitlement, entitlement_session, course_card_index, is_unfulfilled_entitlement, is_fulfilled_entitlement, entitlement_available_sessions, entitlement_expiration_date, entitlement_expired_at, show_courseware_link, cert_status, can_refund_entitlement, can_unenroll, credit_status, show_email_settings, course_mode_info, is_paid_course, is_course_blocked, verification_status, course_requirements, dashboard_index, share_settings, related_programs, display_course_modes_on_dashboard, show_consent_link, enterprise_customer_name" expression_filter="h"/>
<%!
import urllib
@@ -237,7 +237,12 @@ from util.course import get_link_for_about_page, get_encoded_course_sharing_utm_
% endif
% endif
- % if not entitlement:
+ ## Right now, the gear dropdown for entitlements only contains the 'unenroll' link, so we should hide the
+ ## gear altogether if the user is unable to unenroll/refund their entitlement. Later, when we add more options
+ ## to the gear dropdown, we can remove this check.
+ % if entitlement and can_refund_entitlement:
+ <%include file='_dashboard_entitlement_actions.html' args='course_overview=course_overview,entitlement=entitlement,dashboard_index=dashboard_index, can_refund_entitlement=can_refund_entitlement'/>
+ % elif not entitlement:
+
+<%include file="dashboard/_dashboard_entitlement_unenrollment_modal.html"/>