Add 'View Consent' button to dashboard when required
Enterprise customers can require user to agree to Data Sharing Consent form before they can access a course. We now add it conditionally to Course Dashboard when it's required so it's apparent to user and they have a way to revist the consent form if they've previously declined or the course has not yet started. WL-1281
This commit is contained in:
@@ -7,6 +7,7 @@ import json
|
||||
import unittest
|
||||
|
||||
import ddt
|
||||
import mock
|
||||
import pytz
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
@@ -335,3 +336,43 @@ class StudentDashboardTests(SharedModuleStoreTestCase, MilestonesTestCaseMixin):
|
||||
remove_prerequisite_course(self.course.id, get_course_milestones(self.course.id)[0])
|
||||
response = self.client.get(reverse('dashboard'))
|
||||
self.assertNotIn('<div class="prerequisites">', response.content)
|
||||
|
||||
@mock.patch('student.views.consent_needed_for_course')
|
||||
@mock.patch('student.views.enterprise_customer_for_request')
|
||||
@ddt.data(
|
||||
(True, True, True),
|
||||
(True, True, False),
|
||||
(True, False, False),
|
||||
(False, True, False),
|
||||
(False, False, False),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_enterprise_view_consent_for_course(
|
||||
self,
|
||||
enterprise_enabled,
|
||||
consent_needed,
|
||||
future_course,
|
||||
mock_enterprise_customer,
|
||||
mock_consent_necessary
|
||||
):
|
||||
"""
|
||||
Verify that the 'View Consent' icon show up if data sharing consent turned on
|
||||
for enterprise customer
|
||||
"""
|
||||
if future_course:
|
||||
self.course = CourseFactory.create(start=self.TOMORROW, emit_signals=True)
|
||||
else:
|
||||
self.course = CourseFactory.create(emit_signals=True)
|
||||
self.course_enrollment = CourseEnrollmentFactory(course_id=self.course.id, user=self.user)
|
||||
|
||||
if enterprise_enabled:
|
||||
mock_enterprise_customer.return_value = {'name': 'TestEnterprise', 'uuid': 'abc123xxx'}
|
||||
else:
|
||||
mock_enterprise_customer.return_value = None
|
||||
|
||||
mock_consent_necessary.return_value = consent_needed
|
||||
|
||||
# Assert 'View Consent' button shows up appropriately
|
||||
response = self.client.get(reverse('dashboard'))
|
||||
self.assertEquals('View Consent' in response.content, enterprise_enabled and consent_needed)
|
||||
self.assertEquals('TestEnterprise' in response.content, enterprise_enabled and consent_needed)
|
||||
|
||||
@@ -87,7 +87,11 @@ from openedx.core.djangoapps.theming import helpers as theming_helpers
|
||||
from openedx.core.djangoapps.user_api.preferences import api as preferences_api
|
||||
from openedx.core.djangolib.markup import HTML
|
||||
from openedx.features.course_experience import course_home_url_name
|
||||
from openedx.features.enterprise_support.api import get_dashboard_consent_notification
|
||||
from openedx.features.enterprise_support.api import (
|
||||
consent_needed_for_course,
|
||||
enterprise_customer_for_request,
|
||||
get_dashboard_consent_notification
|
||||
)
|
||||
from shoppingcart.api import order_history
|
||||
from shoppingcart.models import CourseRegistrationCode, DonationConfiguration
|
||||
from student.cookies import delete_logged_in_cookies, set_logged_in_cookies, set_user_info_cookie
|
||||
@@ -729,6 +733,16 @@ def dashboard(request):
|
||||
|
||||
enterprise_message = get_dashboard_consent_notification(request, user, course_enrollments)
|
||||
|
||||
enterprise_customer = enterprise_customer_for_request(request)
|
||||
consent_required_courses = set()
|
||||
enterprise_customer_name = None
|
||||
if enterprise_customer:
|
||||
consent_required_courses = {
|
||||
enrollment.course_id for enrollment in course_enrollments
|
||||
if consent_needed_for_course(request, request.user, str(enrollment.course_id), True)
|
||||
}
|
||||
enterprise_customer_name = enterprise_customer['name']
|
||||
|
||||
# Account activation message
|
||||
account_activation_messages = [
|
||||
message for message in messages.get_messages(request) if 'account-activation' in message.tags
|
||||
@@ -847,6 +861,8 @@ def dashboard(request):
|
||||
|
||||
context = {
|
||||
'enterprise_message': enterprise_message,
|
||||
'consent_required_courses': consent_required_courses,
|
||||
'enterprise_customer_name': enterprise_customer_name,
|
||||
'enrollment_message': enrollment_message,
|
||||
'redirect_message': redirect_message,
|
||||
'account_activation_messages': account_activation_messages,
|
||||
|
||||
@@ -721,7 +721,7 @@
|
||||
|
||||
@include clearfix();
|
||||
|
||||
position: relative;
|
||||
position: inherit;
|
||||
|
||||
@include left($baseline/2);
|
||||
@include padding(($baseline * 0.4), 0, ($baseline * 0.4), ($baseline * 0.75));
|
||||
@@ -772,6 +772,15 @@
|
||||
opacity: 0.875;
|
||||
}
|
||||
}
|
||||
|
||||
.action-view-consent {
|
||||
@extend %btn-pl-white-base;
|
||||
@include float(right);
|
||||
|
||||
&.archived {
|
||||
@extend %btn-pl-default-base;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TYPE: status
|
||||
|
||||
@@ -128,7 +128,8 @@ from openedx.core.djangolib.markup import HTML, Text
|
||||
<% course_verification_status = verification_status_by_course.get(enrollment.course_id, {}) %>
|
||||
<% course_requirements = courses_requirements_not_met.get(enrollment.course_id) %>
|
||||
<% related_programs = inverted_programs.get(unicode(enrollment.course_id)) %>
|
||||
<%include file='dashboard/_dashboard_course_listing.html' args='course_overview=enrollment.course_overview, enrollment=enrollment, 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 = (enrollment.course_id in consent_required_courses) %>
|
||||
<%include file='dashboard/_dashboard_course_listing.html' args='course_overview=enrollment.course_overview, enrollment=enrollment, 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' />
|
||||
% endfor
|
||||
|
||||
</ul>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<%page args="course_overview, enrollment, 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" expression_filter="h"/>
|
||||
<%page args="course_overview, enrollment, 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"/>
|
||||
|
||||
<%!
|
||||
import urllib
|
||||
@@ -289,110 +289,112 @@ from util.course import get_link_for_about_page, get_encoded_course_sharing_utm_
|
||||
<%include file="_dashboard_credit_info.html" args="credit_status=credit_status"/>
|
||||
% endif
|
||||
|
||||
% if verification_status.get('status') in [VERIFY_STATUS_NEED_TO_VERIFY, VERIFY_STATUS_SUBMITTED, VERIFY_STATUS_RESUBMITTED, VERIFY_STATUS_APPROVED, VERIFY_STATUS_NEED_TO_REVERIFY] and not is_course_blocked:
|
||||
<div class="message message-status wrapper-message-primary is-shown">
|
||||
% if verification_status['status'] == VERIFY_STATUS_NEED_TO_VERIFY:
|
||||
<div class="verification-reminder">
|
||||
% if verification_status['days_until_deadline'] is not None:
|
||||
<h4 class="message-title">${_('Verification not yet complete.')}</h4>
|
||||
<p class="message-copy">${ungettext(
|
||||
'You only have {days} day left to verify for this course.',
|
||||
'You only have {days} days left to verify for this course.',
|
||||
verification_status['days_until_deadline']
|
||||
).format(days=verification_status['days_until_deadline'])}</p>
|
||||
% else:
|
||||
<h4 class="message-title">${_('Almost there!')}</h4>
|
||||
<p class="message-copy">${_('You still need to verify for this course.')}</p>
|
||||
% if is_course_blocked:
|
||||
<p id="block-course-msg" class="course-block">
|
||||
${Text(_("You can no longer access this course because payment has not yet been received. "
|
||||
"You can {contact_link_start}contact the account holder{contact_link_end} "
|
||||
"to request payment, or you can "
|
||||
"{unenroll_link_start}unenroll{unenroll_link_end} "
|
||||
"from this course")).format(
|
||||
contact_link_start=HTML('<button type="button">'),
|
||||
contact_link_end=HTML('</button>'),
|
||||
unenroll_link_start=HTML(
|
||||
'<a id="unregister_block_course" rel="leanModal" '
|
||||
'data-course-id="{course_id}" data-course-number="{course_number}" data-course-name="{course_name}" '
|
||||
'href="#unenroll-modal">'
|
||||
).format(
|
||||
course_id=course_overview.id,
|
||||
course_number=course_overview.number,
|
||||
course_name=course_overview.display_name_with_default,
|
||||
),
|
||||
unenroll_link_end=HTML('</a>'),
|
||||
)}
|
||||
</p>
|
||||
% else:
|
||||
% if show_consent_link:
|
||||
<%include file="_dashboard_show_consent.html" args="course_overview=course_overview, course_target=course_target, enrollment=enrollment, enterprise_customer_name=enterprise_customer_name"/>
|
||||
%endif
|
||||
|
||||
% if verification_status.get('status') in [VERIFY_STATUS_NEED_TO_VERIFY, VERIFY_STATUS_SUBMITTED, VERIFY_STATUS_RESUBMITTED, VERIFY_STATUS_APPROVED, VERIFY_STATUS_NEED_TO_REVERIFY]:
|
||||
<div class="message message-status wrapper-message-primary is-shown">
|
||||
% if verification_status['status'] == VERIFY_STATUS_NEED_TO_VERIFY:
|
||||
<div class="verification-reminder">
|
||||
% if verification_status['days_until_deadline'] is not None:
|
||||
<h4 class="message-title">${_('Verification not yet complete.')}</h4>
|
||||
<p class="message-copy">${ungettext(
|
||||
'You only have {days} day left to verify for this course.',
|
||||
'You only have {days} days left to verify for this course.',
|
||||
verification_status['days_until_deadline']
|
||||
).format(days=verification_status['days_until_deadline'])}</p>
|
||||
% else:
|
||||
<h4 class="message-title">${_('Almost there!')}</h4>
|
||||
<p class="message-copy">${_('You still need to verify for this course.')}</p>
|
||||
% endif
|
||||
</div>
|
||||
<div class="verification-cta">
|
||||
<a href="${reverse('verify_student_verify_now', kwargs={'course_id': unicode(course_overview.id)})}" class="btn" data-course-id="${course_overview.id}">${_('Verify Now')}</a>
|
||||
</div>
|
||||
% elif verification_status['status'] == VERIFY_STATUS_SUBMITTED:
|
||||
<h4 class="message-title">${_('You have submitted your verification information.')}</h4>
|
||||
<p class="message-copy">${_('You will see a message on your dashboard when the verification process is complete (usually within 1-2 days).')}</p>
|
||||
% elif verification_status['status'] == VERIFY_STATUS_RESUBMITTED:
|
||||
<h4 class="message-title">${_('Your current verification will expire soon!')}</h4>
|
||||
<p class="message-copy">${_('You have submitted your reverification information. You will see a message on your dashboard when the verification process is complete (usually within 1-2 days).')}</p>
|
||||
% elif verification_status['status'] == VERIFY_STATUS_APPROVED:
|
||||
<h4 class="message-title">${_('You have successfully verified your ID with edX')}</h4>
|
||||
% if verification_status.get('verification_good_until') is not None:
|
||||
<p class="message-copy">${_('Your current verification is effective until {date}.').format(date=verification_status['verification_good_until'])}
|
||||
% endif
|
||||
</div>
|
||||
<div class="verification-cta">
|
||||
<a href="${reverse('verify_student_verify_now', kwargs={'course_id': unicode(course_overview.id)})}" class="btn" data-course-id="${course_overview.id}">${_('Verify Now')}</a>
|
||||
</div>
|
||||
% elif verification_status['status'] == VERIFY_STATUS_SUBMITTED:
|
||||
<h4 class="message-title">${_('You have submitted your verification information.')}</h4>
|
||||
<p class="message-copy">${_('You will see a message on your dashboard when the verification process is complete (usually within 1-2 days).')}</p>
|
||||
% elif verification_status['status'] == VERIFY_STATUS_RESUBMITTED:
|
||||
<h4 class="message-title">${_('Your current verification will expire soon!')}</h4>
|
||||
<p class="message-copy">${_('You have submitted your reverification information. You will see a message on your dashboard when the verification process is complete (usually within 1-2 days).')}</p>
|
||||
% elif verification_status['status'] == VERIFY_STATUS_APPROVED:
|
||||
<h4 class="message-title">${_('You have successfully verified your ID with edX')}</h4>
|
||||
% if verification_status.get('verification_good_until') is not None:
|
||||
<p class="message-copy">${_('Your current verification is effective until {date}.').format(date=verification_status['verification_good_until'])}
|
||||
% elif verification_status['status'] == VERIFY_STATUS_NEED_TO_REVERIFY:
|
||||
<h4 class="message-title">${_('Your current verification will expire soon.')}</h4>
|
||||
## Translators: start_link and end_link will be replaced with HTML tags;
|
||||
## please do not translate these.
|
||||
<p class="message-copy">${Text(_('Your current verification will expire in {days} days. {start_link}Re-verify your identity now{end_link} using a webcam and a government-issued photo ID.')).format(
|
||||
start_link=HTML('<a href="{href}">').format(href=reverse('verify_student_reverify')),
|
||||
end_link=HTML('</a>'),
|
||||
days=settings.VERIFY_STUDENT.get("EXPIRING_SOON_WINDOW")
|
||||
)}
|
||||
</p>
|
||||
% endif
|
||||
% elif verification_status['status'] == VERIFY_STATUS_NEED_TO_REVERIFY:
|
||||
<h4 class="message-title">${_('Your current verification will expire soon.')}</h4>
|
||||
## Translators: start_link and end_link will be replaced with HTML tags;
|
||||
## please do not translate these.
|
||||
<p class="message-copy">${Text(_('Your current verification will expire in {days} days. {start_link}Re-verify your identity now{end_link} using a webcam and a government-issued photo ID.')).format(
|
||||
start_link=HTML('<a href="{href}">').format(href=reverse('verify_student_reverify')),
|
||||
end_link=HTML('</a>'),
|
||||
days=settings.VERIFY_STUDENT.get("EXPIRING_SOON_WINDOW")
|
||||
)}
|
||||
</p>
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
% endif
|
||||
|
||||
% if course_mode_info['show_upsell'] and not is_course_blocked:
|
||||
% if course_mode_info['show_upsell']:
|
||||
<div class="message message-upsell has-actions is-shown">
|
||||
|
||||
<div class="wrapper-extended">
|
||||
<p class="message-copy" align="justify">
|
||||
<b class="message-copy-bold">
|
||||
${_("Pursue a {cert_name_long} to highlight the knowledge and skills you gain in this course.").format(cert_name_long=cert_name_long)}
|
||||
</b><br>
|
||||
${Text(_("It's official. It's easily shareable. "
|
||||
"It's a proven motivator to complete the course. {line_break}"
|
||||
"{link_start}Learn more about the verified {cert_name_long}{link_end}.")).format(
|
||||
line_break=HTML('<br>'),
|
||||
link_start=HTML('<a href="{}" class="verified-info" data-course-key="{}">').format(
|
||||
marketing_link('WHAT_IS_VERIFIED_CERT'),
|
||||
enrollment.course_id
|
||||
),
|
||||
link_end=HTML('</a>'),
|
||||
cert_name_long=cert_name_long
|
||||
)}
|
||||
</p>
|
||||
<div class="action-upgrade-container">
|
||||
% 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}" data-user="${user.username}">
|
||||
% endif
|
||||
<span class="action-upgrade-icon" aria-hidden="true"></span>
|
||||
<span class="wrapper-copy">
|
||||
<span class="copy" id="upgrade-to-verified">${_("Upgrade to Verified")}</span>
|
||||
<span class="sr"> ${_(course_overview.display_name_with_default)}</span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<p class="message-copy" align="justify">
|
||||
<b class="message-copy-bold">
|
||||
${_("Pursue a {cert_name_long} to highlight the knowledge and skills you gain in this course.").format(cert_name_long=cert_name_long)}
|
||||
</b><br>
|
||||
${Text(_("It's official. It's easily shareable. "
|
||||
"It's a proven motivator to complete the course. {line_break}"
|
||||
"{link_start}Learn more about the verified {cert_name_long}{link_end}.")).format(
|
||||
line_break=HTML('<br>'),
|
||||
link_start=HTML('<a href="{}" class="verified-info" data-course-key="{}">').format(
|
||||
marketing_link('WHAT_IS_VERIFIED_CERT'),
|
||||
enrollment.course_id
|
||||
),
|
||||
link_end=HTML('</a>'),
|
||||
cert_name_long=cert_name_long
|
||||
)}
|
||||
</p>
|
||||
<div class="action-upgrade-container">
|
||||
% 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}" data-user="${user.username}">
|
||||
% endif
|
||||
<span class="action-upgrade-icon" aria-hidden="true"></span>
|
||||
<span class="wrapper-copy">
|
||||
<span class="copy" id="upgrade-to-verified">${_("Upgrade to Verified")}</span>
|
||||
<span class="sr"> ${_(course_overview.display_name_with_default)}</span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
%endif
|
||||
|
||||
% if is_course_blocked:
|
||||
<p id="block-course-msg" class="course-block">
|
||||
${Text(_("You can no longer access this course because payment has not yet been received. "
|
||||
"You can {contact_link_start}contact the account holder{contact_link_end} "
|
||||
"to request payment, or you can "
|
||||
"{unenroll_link_start}unenroll{unenroll_link_end} "
|
||||
"from this course")).format(
|
||||
contact_link_start=HTML('<button type="button">'),
|
||||
contact_link_end=HTML('</button>'),
|
||||
unenroll_link_start=HTML(
|
||||
'<a id="unregister_block_course" rel="leanModal" '
|
||||
'data-course-id="{course_id}" data-course-number="{course_number}" data-course-name="{course_name}" '
|
||||
'href="#unenroll-modal">'
|
||||
).format(
|
||||
course_id=course_overview.id,
|
||||
course_number=course_overview.number,
|
||||
course_name=course_overview.display_name_with_default,
|
||||
),
|
||||
unenroll_link_end=HTML('</a>'),
|
||||
)}
|
||||
</p>
|
||||
%endif
|
||||
|
||||
% endif
|
||||
% endif
|
||||
|
||||
% if course_requirements:
|
||||
## Multiple pre-requisite courses are not supported on frontend that's why we are pulling first element
|
||||
|
||||
25
lms/templates/dashboard/_dashboard_show_consent.html
Normal file
25
lms/templates/dashboard/_dashboard_show_consent.html
Normal file
@@ -0,0 +1,25 @@
|
||||
<%page expression_filter="h" args="course_overview, course_target, enrollment, enterprise_customer_name" />
|
||||
<%!
|
||||
from django.utils.translation import ugettext as _
|
||||
%>
|
||||
<%namespace name='static' file='../static_content.html'/>
|
||||
|
||||
<div class="message message-upsell has-actions is-shown">
|
||||
<div class="wrapper-extended">
|
||||
<p class="message-copy" align="justify">
|
||||
<b class="message-copy-bold">
|
||||
${_("Consent to share your data")}
|
||||
</b>
|
||||
<br>
|
||||
${_("To access this course, you must first consent to share your learning achievements with {enterprise_customer_name}.").format(enterprise_customer_name=enterprise_customer_name)}
|
||||
</p>
|
||||
<div class="action-upgrade-container">
|
||||
<a class="action action-view-consent" href="${course_target}" data-course-key="${enrollment.course_id}">
|
||||
<span class="wrapper-copy">
|
||||
<span class="copy" id="view-consent">${_("View Consent")}</span>
|
||||
<span class="sr"> ${_(course_overview.display_name_with_default)}</span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user