Commit 1106746f by Jesse Shapiro

Add consent declined banner to dashboard

parent 4c8dc904
......@@ -102,6 +102,7 @@ from util.milestones_helpers import (
)
from util.password_policy_validators import validate_password_strength
from util.enterprise_helpers import get_dashboard_consent_notification
import third_party_auth
from third_party_auth import pipeline, provider
from student.helpers import (
......@@ -684,6 +685,8 @@ def dashboard(request):
{'email': user.email, 'platform_name': platform_name}
)
enterprise_message = get_dashboard_consent_notification(request, user, course_enrollments)
# Global staff can see what courses errored on their dashboard
staff_access = False
errored_courses = {}
......@@ -804,6 +807,7 @@ def dashboard(request):
display_sidebar_on_dashboard = len(order_history_list) or verification_status in valid_verification_statuses
context = {
'enterprise_message': enterprise_message,
'enrollment_message': enrollment_message,
'redirect_message': redirect_message,
'course_enrollments': course_enrollments,
......
......@@ -6,13 +6,16 @@ import logging
from functools import wraps
from django.conf import settings
from django.contrib.auth.models import User
from django.core.cache import cache
from django.core.urlresolvers import reverse
from django.shortcuts import redirect
from django.utils.http import urlencode
from django.core.cache import cache
from django.utils.translation import ugettext as _
from edxmako.shortcuts import render_to_string
from edx_rest_api_client.client import EdxRestApiClient
try:
from enterprise import utils as enterprise_utils
from enterprise.models import EnterpriseCourseEnrollment
from enterprise.utils import consent_necessary_for_course
except ImportError:
pass
......@@ -23,6 +26,7 @@ import hashlib
import six
CONSENT_FAILED_PARAMETER = 'consent_failed'
ENTERPRISE_CUSTOMER_BRANDING_OVERRIDE_DETAILS = 'enterprise_customer_branding_override_details'
LOGGER = logging.getLogger("edx.enterprise_helpers")
......@@ -249,7 +253,14 @@ def get_enterprise_consent_url(request, course_id, user=None, return_to=None):
url_params = {
'course_id': course_id,
'next': request.build_absolute_uri(return_path)
'next': request.build_absolute_uri(return_path),
'failure_url': request.build_absolute_uri(
reverse('dashboard') + '?' + urlencode(
{
CONSENT_FAILED_PARAMETER: course_id
}
)
),
}
querystring = urlencode(url_params)
full_url = reverse('grant_data_sharing_permissions') + '?' + querystring
......@@ -366,3 +377,68 @@ def get_enterprise_learner_data(site, user):
enterprise_learner_data = EnterpriseApiClient().fetch_enterprise_learner_data(site=site, user=user)
if enterprise_learner_data:
return enterprise_learner_data['results']
def get_dashboard_consent_notification(request, user, course_enrollments):
"""
If relevant to the request at hand, create a banner on the dashboard indicating consent failed.
Args:
request: The WSGIRequest object produced by the user browsing to the Dashboard page.
user: The logged-in user
course_enrollments: A list of the courses to be rendered on the Dashboard page.
Returns:
str: Either an empty string, or a string containing the HTML code for the notification banner.
"""
enrollment = None
enterprise_enrollment = None
course_id = request.GET.get(CONSENT_FAILED_PARAMETER)
if course_id:
for course_enrollment in course_enrollments:
if str(course_enrollment.course_id) == course_id:
enrollment = course_enrollment
break
try:
enterprise_enrollment = EnterpriseCourseEnrollment.objects.get(
course_id=course_id,
enterprise_customer_user__user_id=user.id,
)
except EnterpriseCourseEnrollment.DoesNotExist:
pass
if enterprise_enrollment and enrollment:
enterprise_customer = enterprise_enrollment.enterprise_customer_user.enterprise_customer
contact_info = getattr(enterprise_customer, 'contact_email', None)
if contact_info is None:
message_template = _(
'If you have concerns about sharing your data, please contact your administrator '
'at {enterprise_customer_name}.'
)
else:
message_template = _(
'If you have concerns about sharing your data, please contact your administrator '
'at {enterprise_customer_name} at {contact_info}.'
)
message = message_template.format(
enterprise_customer_name=enterprise_customer.name,
contact_info=contact_info,
)
title = _(
'Enrollment in {course_name} was not complete.'
).format(
course_name=enrollment.course_overview.display_name,
)
return render_to_string(
'util/enterprise_consent_declined_notification.html',
{
'title': title,
'message': message,
}
)
return ''
......@@ -13,7 +13,9 @@ from util.enterprise_helpers import (
insert_enterprise_pipeline_elements,
data_sharing_consent_required,
set_enterprise_branding_filter_param,
get_dashboard_consent_notification,
get_enterprise_branding_filter_param,
get_enterprise_consent_url,
get_enterprise_customer_logo_url
)
......@@ -192,3 +194,144 @@ class TestEnterpriseHelpers(unittest.TestCase):
mock_get_consent_url.assert_called_once()
mock_enterprise_enabled.assert_called_once()
mock_consent_necessary.assert_called_once()
@mock.patch('util.enterprise_helpers.consent_needed_for_course')
def test_get_enterprise_consent_url(self, needed_for_course_mock):
"""
Verify that get_enterprise_consent_url correctly builds URLs.
"""
needed_for_course_mock.return_value = True
request_mock = mock.MagicMock(
user=None,
build_absolute_uri=lambda x: 'http://localhost:8000' + x # Don't do it like this in prod. Ever.
)
course_id = 'course-v1:edX+DemoX+Demo_Course'
return_to = 'info'
expected_url = (
'/enterprise/grant_data_sharing_permissions?course_id=course-v1%3AedX%2BDemoX%2BDemo_'
'Course&failure_url=http%3A%2F%2Flocalhost%3A8000%2Fdashboard%3Fconsent_failed%3Dcou'
'rse-v1%253AedX%252BDemoX%252BDemo_Course&next=http%3A%2F%2Flocalhost%3A8000%2Fcours'
'es%2Fcourse-v1%3AedX%2BDemoX%2BDemo_Course%2Finfo'
)
actual_url = get_enterprise_consent_url(request_mock, course_id, return_to=return_to)
self.assertEqual(actual_url, expected_url)
def test_get_dashboard_consent_notification_no_param(self):
"""
Test that the output of the consent notification renderer meets expectations.
"""
request = mock.MagicMock(
GET={}
)
notification_string = get_dashboard_consent_notification(
request, None, None
)
self.assertEqual(notification_string, '')
def test_get_dashboard_consent_notification_no_enrollments(self):
request = mock.MagicMock(
GET={'consent_failed': 'course-v1:edX+DemoX+Demo_Course'}
)
enrollments = []
user = mock.MagicMock(id=1)
notification_string = get_dashboard_consent_notification(
request, user, enrollments,
)
self.assertEqual(notification_string, '')
def test_get_dashboard_consent_notification_no_matching_enrollments(self):
request = mock.MagicMock(
GET={'consent_failed': 'course-v1:edX+DemoX+Demo_Course'}
)
enrollments = [mock.MagicMock(course_id='other_course_id')]
user = mock.MagicMock(id=1)
notification_string = get_dashboard_consent_notification(
request, user, enrollments,
)
self.assertEqual(notification_string, '')
def test_get_dashboard_consent_notification_no_matching_ece(self):
request = mock.MagicMock(
GET={'consent_failed': 'course-v1:edX+DemoX+Demo_Course'}
)
enrollments = [mock.MagicMock(course_id='course-v1:edX+DemoX+Demo_Course')]
user = mock.MagicMock(id=1)
notification_string = get_dashboard_consent_notification(
request, user, enrollments,
)
self.assertEqual(notification_string, '')
@mock.patch('util.enterprise_helpers.EnterpriseCourseEnrollment')
def test_get_dashboard_consent_notification_no_contact_info(self, ece_mock):
mock_get_ece = ece_mock.objects.get
ece_mock.DoesNotExist = Exception
mock_ece = mock_get_ece.return_value
mock_ece.enterprise_customer_user = mock.MagicMock(
enterprise_customer=mock.MagicMock(
contact_email=None
)
)
mock_ec = mock_ece.enterprise_customer_user.enterprise_customer
mock_ec.name = 'Veridian Dynamics'
request = mock.MagicMock(
GET={'consent_failed': 'course-v1:edX+DemoX+Demo_Course'}
)
enrollments = [
mock.MagicMock(
course_id='course-v1:edX+DemoX+Demo_Course',
course_overview=mock.MagicMock(
display_name='edX Demo Course',
)
),
]
user = mock.MagicMock(id=1)
notification_string = get_dashboard_consent_notification(
request, user, enrollments,
)
expected_message = (
'If you have concerns about sharing your data, please contact your '
'administrator at Veridian Dynamics.'
)
self.assertIn(expected_message, notification_string)
expected_header = 'Enrollment in edX Demo Course was not complete.'
self.assertIn(expected_header, notification_string)
@mock.patch('util.enterprise_helpers.EnterpriseCourseEnrollment')
def test_get_dashboard_consent_notification_contact_info(self, ece_mock):
mock_get_ece = ece_mock.objects.get
ece_mock.DoesNotExist = Exception
mock_ece = mock_get_ece.return_value
mock_ece.enterprise_customer_user = mock.MagicMock(
enterprise_customer=mock.MagicMock(
contact_email='v.palmer@veridiandynamics.com'
)
)
mock_ec = mock_ece.enterprise_customer_user.enterprise_customer
mock_ec.name = 'Veridian Dynamics'
request = mock.MagicMock(
GET={'consent_failed': 'course-v1:edX+DemoX+Demo_Course'}
)
enrollments = [
mock.MagicMock(
course_id='course-v1:edX+DemoX+Demo_Course',
course_overview=mock.MagicMock(
display_name='edX Demo Course',
)
),
]
user = mock.MagicMock(id=1)
notification_string = get_dashboard_consent_notification(
request, user, enrollments,
)
expected_message = (
'If you have concerns about sharing your data, please contact your '
'administrator at Veridian Dynamics at v.palmer@veridiandynamics.com.'
)
self.assertIn(expected_message, notification_string)
expected_header = 'Enrollment in edX Demo Course was not complete.'
self.assertIn(expected_header, notification_string)
<%page expression_filter="h"/>
<div class="wrapper-msg urgency-info">
<div class="msg">
<span class="msg-icon fa fa-info-circle" aria-hidden="true"></span>
<div class="msg-content">
<h2 class="title">${ title }</h2>
<div class="copy">
<p class='consent-declined-message'>${ message }</p>
</div>
</div>
</div>
</div>
......@@ -118,6 +118,20 @@
}
}
&.urgency-info {
background: $msg-bg;
.msg {
color: $white;
}
.msg-icon {
font-size: 2.5em;
padding: 20px;
}
.msg-content {
max-width: 80%;
}
}
&.alert {
border-top: 3px solid $alert-color;
}
......
......@@ -73,6 +73,12 @@ from openedx.core.djangolib.markup import HTML, Text
${enrollment_message | n, decode.utf8}
</div>
%endif
%if enterprise_message:
<div class="dashboard-banner">
${ enterprise_message | n, decode.utf8 }
</div>
%endif
</div>
<main id="main" aria-label="Content" tabindex="-1">
......
......@@ -52,7 +52,7 @@ edx-lint==0.4.3
astroid==1.3.8
edx-django-oauth2-provider==1.1.4
edx-django-sites-extensions==2.1.1
edx-enterprise==0.27.6
edx-enterprise==0.28.0
edx-oauth2-provider==1.2.0
edx-opaque-keys==0.4.0
edx-organizations==0.4.3
......
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