Commit a18e0c6b by Will Daly

Rename LinkedIn configuration field to "company_identifier"

Add "source" parameter to LinkedIn add-to-profile URL.

Add platform name to certification name.

Style changes for linked in sharing on dashboard
parent eb2d8c4e
......@@ -16,13 +16,13 @@ import json
import logging
from pytz import UTC
import uuid
from collections import defaultdict
from collections import defaultdict, OrderedDict
import dogstats_wrapper as dog_stats_api
from django.db.models import Q
import pytz
from urllib import urlencode
from django.utils.translation import ugettext_lazy
from django.utils.translation import ugettext as _, ugettext_lazy
from django.conf import settings
from django.utils import timezone
from django.contrib.auth.models import User
......@@ -1442,28 +1442,56 @@ class DashboardConfiguration(ConfigurationModel):
class LinkedInAddToProfileConfiguration(ConfigurationModel):
"""
LinkedIn Add to Profile Configuration
This configuration enables the "Add to Profile" LinkedIn
button on the student dashboard. The button appears when
users have a certificate available; when clicked,
users are sent to the LinkedIn site with a pre-filled
form allowing them to add the certificate to their
LinkedIn profile.
"""
# tracking code field
dashboard_tracking_code = models.TextField(
blank=True,
MODE_TO_CERT_NAME = {
"honor": ugettext_lazy(u"{platform_name} Honor Code Certificate for {course_name}"),
"verified": ugettext_lazy(u"{platform_name} Verified Certificate for {course_name}"),
"professional": ugettext_lazy(u"{platform_name} Professional Certificate for {course_name}"),
}
company_identifier = models.TextField(
help_text=ugettext_lazy(
u"A dashboard tracking code field for LinkedIn Add-to-profile Certificates. "
u"The company identifier for the LinkedIn Add-to-Profile button "
u"e.g 0_0dPSPyS070e0HsE9HNz_13_d11_"
)
)
@classmethod
def linked_in_dashboard_tracking_code_url(cls, params):
"""
Get the linked-in Configuration.
"""
config = cls.current()
if config.enabled:
return u'http://www.linkedin.com/profile/add?_ed={tracking_code}&{params}'.format(
tracking_code=config.dashboard_tracking_code,
params=urlencode(params)
)
return None
def add_to_profile_url(self, course_name, enrollment_mode, cert_url, source="o"):
"""Construct the URL for the "add to profile" button.
def __unicode__(self):
return self.dashboard_tracking_code
Arguments:
course_name (unicode): The display name of the course.
enrollment_mode (str): The enrollment mode of the user (e.g. "verified", "honor", "professional")
cert_url (str): The download URL for the certificate.
Keyword Arguments:
source (str): Either "o" (for onsite/UI), "e" (for emails), or "m" (for mobile)
"""
params = OrderedDict([
('_ed', self.company_identifier),
('pfCertificationName', self._cert_name(course_name, enrollment_mode).encode('utf-8')),
('pfCertificationUrl', cert_url),
('source', source)
])
return u'http://www.linkedin.com/profile/add?{params}'.format(
params=urlencode(params)
)
def _cert_name(self, course_name, enrollment_mode):
"""Name of the certification, for display on LinkedIn. """
return self.MODE_TO_CERT_NAME.get(
enrollment_mode,
_(u"{platform_name} Certificate for {course_name}")
).format(
platform_name=settings.PLATFORM_NAME,
course_name=course_name
)
......@@ -9,6 +9,7 @@ from datetime import datetime, timedelta
import logging
import pytz
import unittest
import ddt
from django.conf import settings
from django.contrib.auth.models import User, AnonymousUser
......@@ -44,9 +45,17 @@ from config_models.models import cache
log = logging.getLogger(__name__)
@ddt.ddt
class CourseEndingTest(TestCase):
"""Test things related to course endings: certificates, surveys, etc"""
def setUp(self):
super(CourseEndingTest, self).setUp()
# Clear the model-based config cache to avoid
# interference between tests.
cache.clear()
def test_process_survey_link(self):
username = "fred"
user = Mock(username=username)
......@@ -194,8 +203,11 @@ class CourseEndingTest(TestCase):
user = Mock(username="fred")
survey_url = "http://a_survey.com"
course = Mock(end_of_course_survey_url=survey_url, certificates_display_behavior='end')
course.display_name = u'edx/abc/courseregisters®'
course = Mock(
end_of_course_survey_url=survey_url,
certificates_display_behavior='end',
display_name=u'edx/abc/courseregisters®'
)
download_url = 'http://s3.edx/cert'
cert_status = {
......@@ -204,25 +216,28 @@ class CourseEndingTest(TestCase):
'mode': 'honor'
}
LinkedInAddToProfileConfiguration(
dashboard_tracking_code='0_mC_o2MizqdtZEmkVXjH4eYwMj4DnkCWrZP_D9',
enabled=True).save()
company_identifier='0_mC_o2MizqdtZEmkVXjH4eYwMj4DnkCWrZP_D9',
enabled=True
).save()
status_dict = _cert_info(user, course, cert_status, 'honor')
self.assertIn(
'http://www.linkedin.com/profile/add?_ed=0_mC_o2MizqdtZEmkVXjH4eYwMj4DnkCWrZP_D9',
status_dict['linked_in_url']
expected_url = (
'http://www.linkedin.com/profile/add'
'?_ed=0_mC_o2MizqdtZEmkVXjH4eYwMj4DnkCWrZP_D9&'
'pfCertificationName=edX+Honor+Code+Certificate+for+edx%2Fabc%2Fcourseregisters%C2%AE&'
'pfCertificationUrl=http%3A%2F%2Fs3.edx%2Fcert&'
'source=o'
)
self.assertIn('pfCertificationName', status_dict['linked_in_url'])
self.assertIn('pfCertificationUrl', status_dict['linked_in_url'])
self.assertIn('courseregisters', status_dict['linked_in_url'])
self.assertIn('Honor+Code+Certificate', status_dict['linked_in_url'])
self.assertEqual(expected_url, status_dict['linked_in_url'])
def test_linked_in_url_not_exists_without_config(self):
# Test case with Linked-In URL empty with if linked-in-config is none.
cache.clear()
user = Mock(username="fred")
survey_url = "http://a_survey.com"
course = Mock(end_of_course_survey_url=survey_url, certificates_display_behavior='end')
course = Mock(
display_name="Demo Course",
end_of_course_survey_url=survey_url,
certificates_display_behavior='end'
)
download_url = 'http://s3.edx/cert'
cert_status = {
......@@ -246,16 +261,54 @@ class CourseEndingTest(TestCase):
}
)
# adding config. linked-in-url will be return
# Enabling the configuration will cause the LinkedIn
# "add to profile" button to appear.
# We need to clear the cache again to make sure we
# pick up the modified configuration.
cache.clear()
LinkedInAddToProfileConfiguration(
dashboard_tracking_code='0_mC_o2MizqdtZEmkVXjH4eYwMj4DnkCWrZP_D9',
enabled=True).save()
company_identifier='0_mC_o2MizqdtZEmkVXjH4eYwMj4DnkCWrZP_D9',
enabled=True
).save()
status_dict = _cert_info(user, course, cert_status, 'honor')
self.assertIn(
'http://www.linkedin.com/profile/add?_ed=0_mC_o2MizqdtZEmkVXjH4eYwMj4DnkCWrZP_D9',
status_dict['linked_in_url']
expected_url = (
'http://www.linkedin.com/profile/add'
'?_ed=0_mC_o2MizqdtZEmkVXjH4eYwMj4DnkCWrZP_D9&'
'pfCertificationName=edX+Verified+Certificate+for+Demo+Course&'
'pfCertificationUrl=http%3A%2F%2Fs3.edx%2Fcert&'
'source=o'
)
self.assertEqual(expected_url, status_dict['linked_in_url'])
@ddt.data(
('honor', 'edX Honor Code Certificate for DemoX'),
('verified', 'edX Verified Certificate for DemoX'),
('professional', 'edX Professional Certificate for DemoX'),
('default_mode', 'edX Certificate for DemoX')
)
@ddt.unpack
def test_linked_in_url_certificate_types(self, cert_mode, cert_name):
user = Mock(username="fred")
course = Mock(
display_name='DemoX',
end_of_course_survey_url='http://example.com',
certificates_display_behavior='end'
)
cert_status = {
'status': 'downloadable',
'grade': '67',
'download_url': 'http://edx.org',
'mode': cert_mode
}
LinkedInAddToProfileConfiguration(
company_identifier="abcd123",
enabled=True
).save()
status_dict = _cert_info(user, course, cert_status, cert_mode)
self.assertIn(cert_name.replace(' ', '+'), status_dict['linked_in_url'])
class DashboardTest(ModuleStoreTestCase):
......@@ -511,8 +564,10 @@ class DashboardTest(ModuleStoreTestCase):
"""
self.client.login(username="jack", password="test")
tracking_code = '0_mC_o2MizqdtZEmkVXjH4eYwMj4DnkCWrZP_D9'
LinkedInAddToProfileConfiguration(dashboard_tracking_code=tracking_code, enabled=True).save()
LinkedInAddToProfileConfiguration(
company_identifier='0_mC_o2MizqdtZEmkVXjH4eYwMj4DnkCWrZP_D9',
enabled=True
).save()
CourseModeFactory.create(
course_id=self.course.id,
......@@ -542,14 +597,14 @@ class DashboardTest(ModuleStoreTestCase):
self.assertEquals(response.status_code, 200)
self.assertIn('Add Certificate to LinkedIn', response.content)
response_url = (
'http://www.linkedin.com/profile/add?_ed='
'{tracking_code}&pfCertificationUrl={download}&pfCertificationName='
'Honor+Code+Certificate+for+{name}'
).format(
tracking_code=tracking_code, download=download_url, name='Omega'
expected_url = (
'http://www.linkedin.com/profile/add'
'?_ed=0_mC_o2MizqdtZEmkVXjH4eYwMj4DnkCWrZP_D9&'
'pfCertificationName=edX+Honor+Code+Certificate+for+Omega&'
'pfCertificationUrl=www.edx.org&'
'source=o'
)
self.assertContains(response, response_url)
self.assertContains(response, expected_url)
class EnrollInCourseTest(TestCase):
......
......@@ -354,30 +354,16 @@ def _cert_info(user, course, cert_status, course_mode):
else:
status_dict['download_url'] = cert_status['download_url']
# getting linkedin URL and then pass the params which appears
# on user profile. if linkedin config is empty don't show the button.
modes_dict = {
"honor": "Honor Code Certificate",
"verified": "Verified Certificate",
"professional": "Professional Certificate",
}
certification_name = u'{type} for {course_name}'.format(
type=modes_dict.get(course_mode, "Certificate"), course_name=course.display_name
).encode('utf-8')
params_dict = {
'pfCertificationName': certification_name,
'pfCertificationUrl': cert_status['download_url'],
}
# following method will construct and return url if current enabled config exists otherwise return None
# In case of None linked-in-button will not appear on dashboard.
status_dict['linked_in_url'] = LinkedInAddToProfileConfiguration.linked_in_dashboard_tracking_code_url(
params_dict
)
# If enabled, show the LinkedIn "add to profile" button
# Clicking this button sends the user to LinkedIn where they
# can add the certificate information to their profile.
linkedin_config = LinkedInAddToProfileConfiguration.current()
if linkedin_config.enabled:
status_dict['linked_in_url'] = linkedin_config.add_to_profile_url(
course.display_name,
cert_status.get('mode'),
cert_status['download_url']
)
if status in ('generating', 'ready', 'notpassing', 'restricted'):
if 'grade' not in cert_status:
......
......@@ -901,11 +901,51 @@
}
&.course-status-certavailable {
background-color: $gray-l5;
border: 0;
.action-certificate {
.message-copy {
width: flex-grid(6, 12);
position: relative;
float: left;
}
.btn {
.actions {
width: flex-grid(6, 12);
position: relative;
@include float(right);
.action {
@include margin(0, 0, ($baseline/2), ($baseline*.75));
float: none;
text-align: center;
&:last-child {
margin-bottom: 0;
}
.btn {
float: none;
}
}
.action-certificate .btn {
@extend %btn-inherited-primary;
@include box-sizing(border-box);
float: none;
border-radius: 3px;
display: block;
@include padding(7px, ($baseline*.75), 7px, ($baseline*.75));
text-align: center;
a:link, a:visited {
color: #fff;
}
}
.action-share .btn {
display: inline;
letter-spacing: 0;
}
}
}
......
......@@ -6,7 +6,7 @@
cert_name_short = course.cert_name_short
if cert_name_short == "":
cert_name_short = settings.CERT_NAME_SHORT
cert_name_long = course.cert_name_long
if cert_name_long == "":
cert_name_long = settings.CERT_NAME_LONG
......@@ -53,13 +53,13 @@ else:
<li class="action action-certificate">
<a class="btn" href="${cert_status['download_url']}"
title="${_('This link will open/download a PDF document')}">
${_("Download Your {cert_name_short} (PDF)").format(cert_name_short=cert_name_short,)}</a></li>
${_("Download {cert_name_short} (PDF)").format(cert_name_short=cert_name_short,)}</a></li>
% if cert_status['linked_in_url']:
<li class="action action-certificate">
<li class="action action-share">
<a class="btn" target="_blank" href="${cert_status['linked_in_url']}"
title="${_('Add to LinkedIn Profile')}">
${_("Add Certificate to LinkedIn.")}</a></li>
title="${_('Add Certificate to LinkedIn Profile')}">
${_("Share on LinkedIn")}</a></li>
% endif
% elif cert_status['show_download_url'] and enrollment.mode == 'verified' and cert_status['mode'] == 'honor':
......
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