Commit e95f2aee by David Baumgold

Remove LinkedIn integration

parent 82d02851
============================
LinkedIn Integration for edX
============================
This package provides a Django application for use with the edX platform which
allows users to post their earned certificates on their LinkedIn profiles. All
functionality is currently provided via a command line interface intended to be
used by a system administrator and called from other scripts.
Basic Flow
----------
The basic flow is as follows:
o A system administrator uses the 'linkedin_login' script to log in to LinkedIn
as a user with email lookup access in the People API. This provides an access
token that can be used by the 'linkedin_findusers' script to check for users
that have LinkedIn accounts.
o A system administrator (or cron job, etc...) runs the 'linkedin_findusers'
script to query the LinkedIn People API, looking for users of edX which have
accounts on LinkedIn.
o A system administrator (or cron job, etc...) runs the 'linkedin_mailusers'
script. This scripts finds all users with LinkedIn accounts who also have
certificates they've earned which they haven't already been emailed about.
Users are then emailed links to add their certificates to their LinkedIn
accounts.
Configuration
-------------
To use this application, first add it to your `INSTALLED_APPS` setting in your
environment config::
INSTALLED_APPS += ('linkedin',)
You will then also need to provide a new key in your settings, `LINKEDIN_API`,
which is a dictionary::
LINKEDIN_API = {
# Needed for API calls
'CLIENT_ID': "FJkdfj93kf93",
'CLIENT_SECRET': "FJ93oldj939rkfj39",
'REDIRECT_URI': "http://my.org.foo",
# Needed to generate certificate links
'COMPANY_NAME': 'Foo',
'COMPANY_ID': "1234567",
# Needed for sending emails
'EMAIL_FROM': "The Team <someone@org.foo>",
'EMAIL_WHITELIST': set(['fred@bedrock.gov', 'barney@bedrock.gov'])
}
`CLIENT_ID`, `CLIENT_SECRET`, and `REDIRECT_URI` all come from your registration
with LinkedIn for API access. `CLIENT_ID` and `CLIENT_SECRET` will be provied
to you by LinkedIn. You will choose `REDIRECT_URI`, and it will be the URI
users are directed to after handling the authorization flow for logging into
LinkedIn and getting an access token.
`COMPANY_NAME` is the name of the LinkedIn profile for the company issuing the
certificate, e.g. 'edX'. `COMPANY_ID` is the LinkedIn ID for the same profile.
This can be found in the URL for the company profile. For exampled, edX's
LinkedIn profile is found at the URL: http://www.linkedin.com/company/2746406
and their `COMPANY_ID` is 2746406.
`EMAIL_FROM` just sets the from address that is used for generated emails.
`EMAIL_WHITELIST` is optional and intended for use in testing. If
`EMAIL_WHITELIST` is given, only users whose email is in the whitelest will get
notification emails. All others will be skipped. Do not provide this in
production.
If you are adding this application to an already running instance of edX, you
will need to use the `syncdb` script to add the tables used by this application
to the database.
Logging into LinkedIn
---------------------
The management script, `linkedin_login`, interactively guides a user to log into
LinkedIn and obtain an access token. The script generates an authorization URL,
asks the user go to that URL in their web browser and log in via LinkedIn's web
UI. When the user has done that, they will be redirected to the configured
location with an authorization token embedded in the query string of the URL.
This authorization token is good for only 30 seconds. Within 30 seconds the
user should copy and paste the URL they were directed to back into the command
line script, which will then obtain and store an access token.
Access tokens are good for 60 days. There is currently no way to refresh an
access token without rerunning the `linkedin_login` script again.
Finding Users
-------------
Once you have logged in, the management script, `linkedin_findusers`, is used
to find out which users have LinkedIn accounts using LinkedIn's People API. By
default only users which have never been checked are checked. The `--recheck`
option can be provided to recheck all users, in case some users have joined
LinkedIn since the last time they were checked.
LinkedIn has provided guidance on what limits we should follow in accessing
their API based on time of the day and day of the week. The script attempts to
enforce that. To override its enforcement, you can provide the `--force` flag.
Send Emails
-----------
Once you have found users, you can email them links for their earned
certificates using the `linkedin_mailusers` script. The script will only mail
any particular user once for any particular certificate they have earned.
The emails come in two distinct flavors: triggered and grandfathered. Triggered
emails are the default. These comprise one email per earned certificate and are
intended for use when a user has recently earned a certificate, as will
generally be the case if this script is run regularly.
The grandfathered from of the email can be sent by adding the `--grandfather`
flag and is intended to bring users up to speed with all of their earned
certificates at once when this feature is first added to edX.
"""
Send emails to users inviting them to add their course certificates to their
LinkedIn profiles.
"""
from smtplib import SMTPServerDisconnected, SMTPDataError, SMTPConnectError, SMTPException
import json
import logging
import urllib
from boto.exception import AWSConnectionError
from boto.ses.exceptions import (
SESAddressNotVerifiedError,
SESIdentityNotVerifiedError,
SESDomainNotConfirmedError,
SESAddressBlacklistedError,
SESDailyQuotaExceededError,
SESMaxSendingRateExceededError,
SESDomainEndsWithDotError,
SESLocalAddressCharacterError,
SESIllegalAddressError,
)
from django.conf import settings
from django.core.mail import EmailMessage
from django.core.management.base import BaseCommand
from django.db import transaction
from django.template import Context
from django.template.loader import get_template
from django.core.urlresolvers import reverse
from optparse import make_option
from edxmako.shortcuts import render_to_string
from certificates.models import GeneratedCertificate
from courseware.courses import get_course_by_id, course_image_url
from ...models import LinkedIn
# The following is blatantly cribbed from bulk_email/tasks.py
# Errors that an individual email is failing to be sent, and should just
# be treated as a fail.
SINGLE_EMAIL_FAILURE_ERRORS = (
SESAddressBlacklistedError, # Recipient's email address has been temporarily blacklisted.
SESDomainEndsWithDotError, # Recipient's email address' domain ends with a period/dot.
SESIllegalAddressError, # Raised when an illegal address is encountered.
SESLocalAddressCharacterError, # An address contained a control or whitespace character.
)
# Exceptions that, if caught, should cause the task to be re-tried.
# These errors will be caught a limited number of times before the task fails.
LIMITED_RETRY_ERRORS = (
SMTPConnectError,
SMTPServerDisconnected,
AWSConnectionError,
)
# Errors that indicate that a mailing task should be retried without limit.
# An example is if email is being sent too quickly, but may succeed if sent
# more slowly. When caught by a task, it triggers an exponential backoff and retry.
# Retries happen continuously until the email is sent.
# Note that the SMTPDataErrors here are only those within the 4xx range.
# Those not in this range (i.e. in the 5xx range) are treated as hard failures
# and thus like SINGLE_EMAIL_FAILURE_ERRORS.
INFINITE_RETRY_ERRORS = (
SESMaxSendingRateExceededError, # Your account's requests/second limit has been exceeded.
SMTPDataError,
)
# Errors that are known to indicate an inability to send any more emails,
# and should therefore not be retried. For example, exceeding a quota for emails.
# Also, any SMTP errors that are not explicitly enumerated above.
BULK_EMAIL_FAILURE_ERRORS = (
SESAddressNotVerifiedError, # Raised when a "Reply-To" address has not been validated in SES yet.
SESIdentityNotVerifiedError, # Raised when an identity has not been verified in SES yet.
SESDomainNotConfirmedError, # Raised when domain ownership is not confirmed for DKIM.
SESDailyQuotaExceededError, # 24-hour allotment of outbound email has been exceeded.
SMTPException,
)
MAX_ATTEMPTS = 10
log = logging.getLogger("linkedin")
class Command(BaseCommand):
"""
Django command for inviting users to add their course certificates to their
LinkedIn profiles.
"""
args = ''
help = ('Sends emails to edX users that are on LinkedIn who have completed '
'course certificates, inviting them to add their certificates to '
'their LinkedIn profiles')
option_list = BaseCommand.option_list + (
make_option(
'--mock',
action='store_true',
dest='mock_run',
default=False,
help="Run without sending the final e-mails."),)
def __init__(self):
super(Command, self).__init__()
@transaction.commit_manually
def handle(self, *args, **options):
whitelist = settings.LINKEDIN_API['EMAIL_WHITELIST']
mock_run = options.get('mock_run', False)
accounts = LinkedIn.objects.filter(has_linkedin_account=True)
for account in accounts:
user = account.user
if whitelist and user.email not in whitelist:
# Whitelist only certain addresses for testing purposes
continue
try:
emailed = json.loads(account.emailed_courses)
except Exception:
log.exception("LinkedIn: Could not parse emailed_courses for {}".format(user.username))
continue
certificates = GeneratedCertificate.objects.filter(user=user)
certificates = certificates.filter(status='downloadable')
certificates = [cert for cert in certificates if cert.course_id not in emailed]
# Shouldn't happen, since we're only picking users who have
# certificates, but just in case...
if not certificates:
log.info("LinkedIn: No certificates for user {}".format(user.username))
continue
# Basic sanity checks passed, now try to send the emails
try:
success = False
success = self.send_grandfather_email(user, certificates, mock_run)
log.info("LinkedIn: Sent email for user {}".format(user.username))
if not mock_run:
emailed.extend([cert.course_id for cert in certificates])
if success and not mock_run:
account.emailed_courses = json.dumps(emailed)
account.save()
transaction.commit()
except BULK_EMAIL_FAILURE_ERRORS:
log.exception("LinkedIn: No further email sending will work, aborting")
transaction.commit()
return -1
except Exception:
log.exception("LinkedIn: User {} couldn't be processed".format(user.username))
transaction.commit()
def certificate_url(self, certificate):
"""
Generates a certificate URL based on LinkedIn's documentation. The
documentation is from a Word document: DAT_DOCUMENTATION_v3.12.docx
"""
course = get_course_by_id(certificate.course_id)
tracking_code = '-'.join([
'eml',
'prof', # the 'product'--no idea what that's supposed to mean
'edX', # Partner's name
course.number, # Certificate's name
'gf'])
query = [
('pfCertificationName', course.display_name_with_default),
('pfAuthorityId', settings.LINKEDIN_API['COMPANY_ID']),
('pfCertificationUrl', certificate.download_url),
('pfLicenseNo', certificate.course_id),
('pfCertStartDate', course.start.strftime('%Y%m')),
('_mSplash', '1'),
('trk', tracking_code),
('startTask', 'CERTIFICATION_NAME'),
('force', 'true')]
return 'http://www.linkedin.com/profile/guided?' + urllib.urlencode(query)
def send_grandfather_email(self, user, certificates, mock_run=False):
"""
Send the 'grandfathered' email informing historical students that they
may now post their certificates on their LinkedIn profiles.
"""
courses_list = []
for cert in certificates:
course = get_course_by_id(cert.course_id)
course_url = 'https://{}{}'.format(
settings.SITE_NAME,
reverse('course_root', kwargs={'course_id': cert.course_id})
)
course_title = course.display_name_with_default
course_img_url = 'https://{}{}'.format(settings.SITE_NAME, course_image_url(course))
course_end_date = course.end.strftime('%b %Y')
course_org = course.org
courses_list.append({
'course_url': course_url,
'course_org': course_org,
'course_title': course_title,
'course_image_url': course_img_url,
'course_end_date': course_end_date,
'linkedin_add_url': self.certificate_url(cert),
})
context = {
'courses_list': courses_list,
'num_courses': len(courses_list),
'google_analytics': settings.GOOGLE_ANALYTICS_LINKEDIN,
}
body = render_to_string('linkedin/linkedin_email.html', context)
subject = u'{}, Add your Achievements to your LinkedIn Profile'.format(user.profile.name)
if mock_run:
return True
else:
return self.send_email(user, subject, body)
def send_email(self, user, subject, body, num_attempts=MAX_ATTEMPTS):
"""
Send an email. Return True if it succeeded, False if it didn't.
"""
fromaddr = u'no-reply@notifier.edx.org'
toaddr = u'{} <{}>'.format(user.profile.name, user.email)
msg = EmailMessage(subject, body, fromaddr, (toaddr,))
msg.content_subtype = "html"
i = 1
while i <= num_attempts:
try:
msg.send()
return True # Happy path!
except SINGLE_EMAIL_FAILURE_ERRORS:
# Something unrecoverable is wrong about the email acct we're sending to
log.exception(
u"LinkedIn: Email send failed for user {}, email {}"
.format(user.username, user.email)
)
return False
except LIMITED_RETRY_ERRORS:
# Something went wrong (probably an intermittent connection error),
# but maybe if we beat our heads against the wall enough times,
# we can crack our way through. Thwack! Thwack! Thwack!
# Give up after num_attempts though (for loop exits), let's not
# get carried away.
log.exception(
u"LinkedIn: Email send for user {}, email {}, encountered error, attempt #{}"
.format(user.username, user.email, i)
)
i += 1
continue
except INFINITE_RETRY_ERRORS:
# Dude, it will *totally* work if I just... sleep... a little...
# Things like max send rate exceeded. The smart thing would be
# to do exponential backoff. The lazy thing to do would be just
# sleep some arbitrary amount and trust that it'll probably work.
# GUESS WHAT WE'RE DOING BOYS AND GIRLS!?!
log.exception("LinkedIn: temporary error encountered, retrying")
time.sleep(1)
# If we hit here, we went through all our attempts without success
return False
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'LinkedIn'
db.create_table('linkedin_linkedin', (
('user', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.User'], unique=True, primary_key=True)),
('has_linkedin_account', self.gf('django.db.models.fields.NullBooleanField')(default=None, null=True, blank=True)),
('emailed_courses', self.gf('django.db.models.fields.TextField')(default='[]')),
))
db.send_create_signal('linkedin', ['LinkedIn'])
def backwards(self, orm):
# Deleting model 'LinkedIn'
db.delete_table('linkedin_linkedin')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'linkedin.linkedin': {
'Meta': {'object_name': 'LinkedIn'},
'emailed_courses': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
'has_linkedin_account': ('django.db.models.fields.NullBooleanField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
}
}
complete_apps = ['linkedin']
"""
Models for LinkedIn integration app.
"""
from django.contrib.auth.models import User
from django.db import models
class LinkedIn(models.Model):
"""
Defines a table for storing a users's LinkedIn status.
"""
user = models.OneToOneField(User, primary_key=True)
has_linkedin_account = models.NullBooleanField(default=None)
emailed_courses = models.TextField(default="[]") # JSON list of course ids
{% load i18n %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="initial-scale=1.0">
</head>
<body>
<p>{% blocktrans with name=student_name %}
Dear {{student_name}},
{% endblocktrans %}</p>
<p>{% blocktrans with name=course_name %}
Congratulations on earning your certificate in {{course_name}}!
Since you have an account on LinkedIn, you can display your hard earned
credential for your colleagues to see. Click the button below to add the
certificate to your profile.
{% endblocktrans %}</p>
<p><a href="{{url|safe}}">
<span>in<span>
{% blocktrans %}Add to profile{% endblocktrans %}
</a></p>
</body>
</html>
......@@ -1663,14 +1663,6 @@ PASSWORD_DICTIONARY = []
INSTALLED_APPS += ('django_openid_auth',)
############################ LinkedIn Integration #############################
INSTALLED_APPS += ('linkedin',)
LINKEDIN_API = {
'EMAIL_WHITELIST': [],
'COMPANY_ID': '2746406',
}
############################ ORA 2 ############################################
# By default, don't use a file prefix
......
......@@ -407,9 +407,6 @@ MAKO_TEMPLATES['main'].extend([
])
######### LinkedIn ########
LINKEDIN_API['COMPANY_ID'] = '0000000'
# Setting for the testing of Software Secure Result Callback
VERIFY_STUDENT["SOFTWARE_SECURE"] = {
"API_ACCESS_KEY": "BBBBBBBBBBBBBBBBBBBB",
......
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns:fb="http://www.facebook.com/2008/fbml" xmlns:og="http://opengraph.org/schema/"> <head>
<meta property="og:title" content="Start 2014 with edX! Sign up for brand new courses."/>
<meta property="fb:page_id" content="43929265776" />
## NAME: 1 COLUMN
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Share Your edX Success on LinkedIn</title>
<style type="text/css">
body,#bodyTable,#bodyCell{
height:100% !important;
margin:0;
padding:0;
width:100% !important;
}
table{
border-collapse:collapse;
}
img,a img{
border:0;
outline:none;
text-decoration:none;
}
h1,h2,h3,h4,h5,h6{
margin:0;
padding:0;
}
p{
margin:1em 0;
padding:0;
}
a{
word-wrap:break-word;
}
.ReadMsgBody{
width:100%;
}
.ExternalClass{
width:100%;
}
.ExternalClass,.ExternalClass p,.ExternalClass span,.ExternalClass font,.ExternalClass td,.ExternalClass div{
line-height:100%;
}
table,td{
mso-table-lspace:0pt;
mso-table-rspace:0pt;
}
#outlook a{
padding:0;
}
img{
-ms-interpolation-mode:bicubic;
}
body,table,td,p,a,li,blockquote{
-ms-text-size-adjust:100%;
-webkit-text-size-adjust:100%;
}
#bodyCell{
padding:20px;
}
.mcnImage{
vertical-align:bottom;
}
.mcnTextContent img{
height:auto !important;
}
body,#bodyTable{
background-color:#f5f5f5;
}
#bodyCell{
border-top:0;
}
#templateContainer{
border:0;
}
h1{
color:#606060 !important;
display:block;
font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;
font-size:40px;
font-style:normal;
font-weight:bold;
line-height:125%;
letter-spacing:-1px;
margin:0;
text-align:left;
}
h2{
color:#606060 !important;
display:block;
font-family:Helvetica;
font-size:26px;
font-style:normal;
font-weight:bold;
line-height:125%;
letter-spacing:-.75px;
margin:0;
text-align:left;
}
h3{
color:#606060 !important;
display:block;
font-family:Helvetica;
font-size:18px;
font-style:normal;
font-weight:bold;
line-height:125%;
letter-spacing:-.5px;
margin:0;
text-align:left;
}
h4{
color:#808080 !important;
display:block;
font-family:Helvetica;
font-size:16px;
font-style:normal;
font-weight:bold;
line-height:125%;
letter-spacing:normal;
margin:0;
text-align:left;
}
#templatePreheader{
background-color:#0f6a93;
border-top:0;
border-bottom:0;
}
.preheaderContainer .mcnTextContent,.preheaderContainer .mcnTextContent p{
color:#ffffff;
font-family:Helvetica;
font-size:11px;
line-height:125%;
text-align:left;
}
.preheaderContainer .mcnTextContent a{
color:#f0f0f0;
font-weight:normal;
text-decoration:underline;
}
#templateHeader{
background-color:#ffffff;
border-top:0;
border-bottom:0;
}
.headerContainer .mcnTextContent,.headerContainer .mcnTextContent p{
color:#606060;
font-family:Helvetica;
font-size:15px;
line-height:150%;
text-align:left;
}
.headerContainer .mcnTextContent a{
color:#6DC6DD;
font-weight:normal;
text-decoration:underline;
}
#templateBody{
background-color:#ffffff;
border-top:0;
border-bottom:0;
}
.bodyContainer .mcnTextContent,.bodyContainer .mcnTextContent p{
color:#606060;
font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;
font-size:14px;
line-height:150%;
text-align:left;
}
.bodyContainer .mcnTextContent a{
color:#126f9a;
font-weight:normal;
text-decoration:underline;
}
#templateFooter{
background-color:#0f6a93;
border-top:0;
border-bottom:0;
}
.footerContainer .mcnTextContent,.footerContainer .mcnTextContent p{
color:#f2f2f2;
font-family:Helvetica;
font-size:11px;
line-height:125%;
text-align:left;
}
.footerContainer .mcnTextContent a{
color:#ffffff;
font-weight:normal;
text-decoration:underline;
}
@media only screen and (max-width: 480px){
body,table,td,p,a,li,blockquote{
-webkit-text-size-adjust:none !important;
}
}@media only screen and (max-width: 480px){
body{
width:100% !important;
min-width:100% !important;
}
}@media only screen and (max-width: 480px){
td[id=bodyCell]{
padding:10px !important;
}
}@media only screen and (max-width: 480px){
table[class=mcnTextContentContainer]{
width:100% !important;
}
}@media only screen and (max-width: 480px){
table[class=mcnBoxedTextContentContainer]{
width:100% !important;
}
}@media only screen and (max-width: 480px){
table[class=mcpreview-image-uploader]{
width:100% !important;
display:none !important;
}
}@media only screen and (max-width: 480px){
img[class=mcnImage]{
width:100% !important;
}
}@media only screen and (max-width: 480px){
table[class=mcnImageGroupContentContainer]{
width:100% !important;
}
}@media only screen and (max-width: 480px){
td[class=mcnImageGroupContent]{
padding:9px !important;
}
}@media only screen and (max-width: 480px){
td[class=mcnImageGroupBlockInner]{
padding-bottom:0 !important;
padding-top:0 !important;
}
}@media only screen and (max-width: 480px){
tbody[class=mcnImageGroupBlockOuter]{
padding-bottom:9px !important;
padding-top:9px !important;
}
}@media only screen and (max-width: 480px){
table[class=mcnCaptionTopContent],table[class=mcnCaptionBottomContent]{
width:100% !important;
}
}@media only screen and (max-width: 480px){
table[class=mcnCaptionLeftTextContentContainer],table[class=mcnCaptionRightTextContentContainer],table[class=mcnCaptionLeftImageContentContainer],table[class=mcnCaptionRightImageContentContainer],table[class=mcnImageCardLeftTextContentContainer],table[class=mcnImageCardRightTextContentContainer]{
width:100% !important;
}
}@media only screen and (max-width: 480px){
td[class=mcnImageCardLeftImageContent],td[class=mcnImageCardRightImageContent]{
padding-right:18px !important;
padding-left:18px !important;
padding-bottom:0 !important;
}
}@media only screen and (max-width: 480px){
td[class=mcnImageCardBottomImageContent]{
padding-bottom:9px !important;
}
}@media only screen and (max-width: 480px){
td[class=mcnImageCardTopImageContent]{
padding-top:18px !important;
}
}@media only screen and (max-width: 480px){
td[class=mcnImageCardLeftImageContent],td[class=mcnImageCardRightImageContent]{
padding-right:18px !important;
padding-left:18px !important;
padding-bottom:0 !important;
}
}@media only screen and (max-width: 480px){
td[class=mcnImageCardBottomImageContent]{
padding-bottom:9px !important;
}
}@media only screen and (max-width: 480px){
td[class=mcnImageCardTopImageContent]{
padding-top:18px !important;
}
}@media only screen and (max-width: 480px){
table[class=mcnCaptionLeftContentOuter] td[class=mcnTextContent],table[class=mcnCaptionRightContentOuter] td[class=mcnTextContent]{
padding-top:9px !important;
}
}@media only screen and (max-width: 480px){
td[class=mcnCaptionBlockInner] table[class=mcnCaptionTopContent]:last-child td[class=mcnTextContent]{
padding-top:18px !important;
}
}@media only screen and (max-width: 480px){
td[class=mcnBoxedTextContentColumn]{
padding-left:18px !important;
padding-right:18px !important;
}
}@media only screen and (max-width: 480px){
td[class=mcnTextContent]{
padding-right:18px !important;
padding-left:18px !important;
}
}@media only screen and (max-width: 480px){
table[id=templateContainer],table[id=templatePreheader],table[id=templateHeader],table[id=templateBody],table[id=templateFooter]{
max-width:600px !important;
width:100% !important;
}
}@media only screen and (max-width: 480px){
h1{
font-size:18px !important;
line-height:125% !important;
}
}@media only screen and (max-width: 480px){
h2{
font-size:16px !important;
line-height:125% !important;
}
}@media only screen and (max-width: 480px){
h3{
font-size:14px !important;
line-height:125% !important;
}
}@media only screen and (max-width: 480px){
h4{
font-size:12px !important;
line-height:125% !important;
}
}@media only screen and (max-width: 480px){
table[class=mcnBoxedTextContentContainer] td[class=mcnTextContent],td[class=mcnBoxedTextContentContainer] td[class=mcnTextContent] p{
font-size:18px !important;
line-height:125% !important;
}
}@media only screen and (max-width: 480px){
table[id=templatePreheader]{
display:block !important;
}
}@media only screen and (max-width: 480px){
td[class=preheaderContainer] td[class=mcnTextContent],td[class=preheaderContainer] td[class=mcnTextContent] p{
font-size:14px !important;
line-height:115% !important;
}
}@media only screen and (max-width: 480px){
td[class=headerContainer] td[class=mcnTextContent],td[class=headerContainer] td[class=mcnTextContent] p{
font-size:18px !important;
line-height:125% !important;
}
}@media only screen and (max-width: 480px){
td[class=bodyContainer] td[class=mcnTextContent],td[class=bodyContainer] td[class=mcnTextContent] p{
font-size:18px !important;
line-height:125% !important;
}
}@media only screen and (max-width: 480px){
td[class=footerContainer] td[class=mcnTextContent],td[class=footerContainer] td[class=mcnTextContent] p{
font-size:14px !important;
line-height:115% !important;
}
}@media only screen and (max-width: 480px){
td[class=footerContainer] a[class=utilityLink]{
display:block !important;
}
}</style> <script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
try {
var _gaq = _gaq || [];
_gaq.push(["_setAccount", "${google_analytics}"]);
_gaq.push(["_setDomainName", ".campaign-archive.com"]);
_gaq.push(["_trackPageview"]);
_gaq.push(["_setAllowLinker", true]);
} catch(err) {console.log(err);}</script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script> <script src="http://us5.campaign-archive1.com/js/mailchimp/fancyzoom.mc.js"></script> <script type="text/javascript">
function incrementFacebookLikeCount() {
var current = parseInt($('#campaign-fb-like-btn span').html());
$('#campaign-fb-like-btn span').fadeOut().html(++current).fadeIn();
}
function getUrlParams(str) {
var vars = {}, hash;
if (!str) return vars;
var hashes = str.slice(str.indexOf('?') + 1).split('&');
for(var i = 0; i < hashes.length; i++) {
hash = hashes[i].split('=');
vars[hash[0]] = hash[1];
}
return vars;
}
function setupSocialSharingStuffs() {
var numSocialElems = $('a[rel=socialproxy]').length;
var numSocialInitialized = 0;
var urlParams = getUrlParams(window.document.location.href);
var paramsToCopy = {'e':true, 'eo':true};
$('a[rel=socialproxy]').each(function() {
var href = $(this).attr('href');
var newHref = decodeURIComponent(href.match(/socialproxy=(.*)/)[1]);
// for facebook insanity to work well, it needs to all be run against just campaign-archive
newHref = newHref.replace(/campaign-archive(\d)/gi, 'campaign-archive');
var newHrefParams = getUrlParams(newHref);
for(var param in urlParams) {
if ((param in paramsToCopy) && !(param in newHrefParams)) {
newHref += '&' + param + '=' + urlParams[param];
}
}
$(this).attr('href', newHref);
if (href.indexOf('facebook-comment') !== -1) {
$(this).fancyZoom({"zoom_id": "social-proxy", "width":620, "height":450, "iframe_height": 450});
} else {
$(this).fancyZoom({"zoom_id": "social-proxy", "width":500, "height":200, "iframe_height": 500});
}
numSocialInitialized++;
});
}
if (window.top!=window.self){
$(function() {
var iframeOffset = $("#archive", window.parent.document).offset();
$("a").each(function () {
var link = $(this);
var href = link.attr("href");
if (href && href[0] == "#") {
var name = href.substring(1);
$(this).click(function () {
var nameElement = $("[name='" + name + "']");
var idElement = $("#" + name);
var element = null;
if (nameElement.length > 0) {
element = nameElement;
} else if (idElement.length > 0) {
element = idElement;
}
if (element) {
var offset = element.offset();
var height = element.height();
//3 is totally arbitrary, but seems to work best.
window.parent.scrollTo(offset.left, (offset.top + iframeOffset.top - (height*3)) );
}
return false;
});
}
});
});
}
</script> <script type="text/javascript">
$(document).ready(function() {
setupSocialSharingStuffs();
});
</script> <style type="text/css">
/* Facebook/Google+ Modals */
#social-proxy { background:#fff; -webkit-box-shadow: 4px 4px 8px 2px rgba(0,0,0,.2); box-shadow: 4px 4px 8px 2px rgba(0,0,0,.2); padding-bottom:35px; z-index:1000; }
#social-proxy_close { display:block; position:absolute; top:0; right:0; height:30px; width:32px; background:transparent url(http://cdn-images.mailchimp.com/awesomebar-sprite.png) 0 -200px; text-indent:-9999px; outline:none; font-size:1px; }
#social-proxy_close:hover { background-position:0 -240px; }
body { padding-bottom:50px !important; }
</style> </head>
<body leftmargin="0" marginwidth="0" topmargin="0" marginheight="0" offset="0" style="margin: 0;padding: 0;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;background-color: #f5f5f5;height: 100% !important;width: 100% !important;">
<center>
<table align="center" border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" id="bodyTable" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;margin: 0;padding: 0;background-color: #f5f5f5;height: 100% !important;width: 100% !important;">
<tr>
<td align="center" valign="top" id="bodyCell" style="mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;margin: 0;padding: 20px;border-top: 0;height: 100% !important;width: 100% !important;">
## BEGIN TEMPLATE //
<table border="0" cellpadding="0" cellspacing="0" width="600" id="templateContainer" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;border: 0;">
<tr>
<td align="center" valign="top" style="mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
## BEGIN PREHEADER //
<table border="0" cellpadding="0" cellspacing="0" width="600" id="templatePreheader" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;background-color: #0f6a93;border-top: 0;border-bottom: 0;">
<tr>
<td valign="top" class="preheaderContainer" style="padding-top: 9px;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;"><table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnTextBlock" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody class="mcnTextBlockOuter">
<tr>
<td valign="top" class="mcnTextBlockInner" style="mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<table align="right" border="0" cellpadding="0" cellspacing="0" width="366" class="mcnTextContentContainer" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody><tr>
<td valign="top" class="mcnTextContent" style="padding-top: 9px;padding-right: 18px;padding-bottom: 9px;padding-left: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #ffffff;font-family: Helvetica;font-size: 11px;line-height: 125%;text-align: left;">
<div style="text-align: right;">&nbsp;<span style="text-align: right; font-size: 11px;"><span style="color:#fofofo;">Connect with us on:</span><span style="color: #00A0E3;">&nbsp;</span></span><span style="text-align: right;">&nbsp;</span><a href="http://facebook.com/edxonline" style="text-align: right;word-wrap: break-word;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #f0f0f0;font-weight: normal;text-decoration: underline;" target="_blank"><img align="none" height="18" src="https://gallery.mailchimp.com/1822a33c054dc20e223ca40e2/images/Facebook_Gray.png" style="width: 18px;height: 18px;border: 0;outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;" width="18"></a><span style="text-align: right;">&nbsp;&nbsp;</span><a href="http://twitter.com/edxonline" style="text-align: right;word-wrap: break-word;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #f0f0f0;font-weight: normal;text-decoration: underline;" target="_blank"><img align="none" height="18" src="https://gallery.mailchimp.com/1822a33c054dc20e223ca40e2/images/Twitter_Gray.png" style="width: 18px;height: 18px;border: 0;outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;" width="18"></a><span style="text-align: right;">&nbsp;&nbsp;</span><a href="https://plus.google.com/108235383044095082735" style="text-align: right;word-wrap: break-word;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #f0f0f0;font-weight: normal;text-decoration: underline;" target="_blank"><img align="none" height="18" src="https://gallery.mailchimp.com/1822a33c054dc20e223ca40e2/images/Google_Gray.png" style="width: 18px;height: 18px;border: 0;outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;" width="18"></a><span style="text-align: right;">&nbsp;&nbsp;</span><a href="http://www.linkedin.com/company/edx?trk=nav_account_sub_nav_company_admin" style="text-align: right;word-wrap: break-word;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #f0f0f0;font-weight: normal;text-decoration: underline;" target="_blank"><img align="none" height="18" src="https://gallery.mailchimp.com/1822a33c054dc20e223ca40e2/images/LinkedIn_Gray.png" style="width: 18px;height: 18px;border: 0;outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;" width="18"></a><span style="text-align: right;">&nbsp;&nbsp;</span><a href="http://vk.com/edxrussia" style="text-align: right;word-wrap: break-word;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #f0f0f0;font-weight: normal;text-decoration: underline;" target="_blank"><img align="none" height="18" src="https://gallery.mailchimp.com/1822a33c054dc20e223ca40e2/images/VK_Grayd3c9a7.png" style="opacity: 0.9;width: 18px;height: 18px;border: 0;outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;" width="18"></a><span style="text-align: right;">&nbsp;&nbsp;</span><a href="http://www.meetup.com/edX-Communities/" style="text-align: right;word-wrap: break-word;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #f0f0f0;font-weight: normal;text-decoration: underline;" target="_blank"><img align="none" height="18" src="https://gallery.mailchimp.com/1822a33c054dc20e223ca40e2/images/Meetup_Gray.png" style="width: 18px;height: 18px;border: 0;outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;" width="18"></a></div>
</td>
</tr>
</tbody></table>
</td>
</tr>
</tbody>
</table></td>
</tr>
</table>
## // END PREHEADER
</td>
</tr>
<tr>
<td align="center" valign="top" style="mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
## BEGIN HEADER //
<table border="0" cellpadding="0" cellspacing="0" width="600" id="templateHeader" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;background-color: #ffffff;border-top: 0;border-bottom: 0;">
<tr>
<td valign="top" class="headerContainer" style="mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;"><table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnImageBlock" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody class="mcnImageBlockOuter">
<tr>
<td valign="top" style="padding: 9px;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;" class="mcnImageBlockInner">
<table align="left" width="100%" border="0" cellpadding="0" cellspacing="0" class="mcnImageContentContainer" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody><tr>
<td class="mcnImageContent" valign="top" style="padding-right: 9px;padding-left: 9px;padding-top: 0;padding-bottom: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<img align="left" alt="edX - Connect To A Better Future" src="https://gallery.mailchimp.com/1822a33c054dc20e223ca40e2/images/edxeb0b919494099d631b.png" width="100" style="max-width: 100px;padding-bottom: 0;display: inline !important;vertical-align: bottom;border: 0;outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;" class="mcnImage">
</td>
</tr>
</tbody></table>
</td>
</tr>
</tbody>
</table><table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnTextBlock" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody class="mcnTextBlockOuter">
<tr>
<td valign="top" class="mcnTextBlockInner" style="mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<table align="left" border="0" cellpadding="0" cellspacing="0" width="600" class="mcnTextContentContainer" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody><tr>
<td valign="top" class="mcnTextContent" style="padding: 9px 18px;line-height: 110%;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #606060;font-family: Helvetica;font-size: 15px;text-align: left;">
<font size="6">Share your edX success on LinkedIn</font>
</td>
</tr>
</tbody></table>
</td>
</tr>
</tbody>
</table></td>
</tr>
</table>
## // END HEADER
</td>
</tr>
<tr>
<td align="center" valign="top" style="mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
## BEGIN BODY //
<table border="0" cellpadding="0" cellspacing="0" width="600" id="templateBody" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;background-color: #ffffff;border-top: 0;border-bottom: 0;">
<tr>
<td valign="top" class="bodyContainer" style="mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;"><table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnTextBlock" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody class="mcnTextBlockOuter">
<tr>
<td valign="top" class="mcnTextBlockInner" style="mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<table align="left" border="0" cellpadding="0" cellspacing="0" width="600" class="mcnTextContentContainer" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody><tr>
<td valign="top" class="mcnTextContent" style="padding-top: 9px;padding-right: 18px;padding-bottom: 9px;padding-left: 18px;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #606060;font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;font-size: 14px;line-height: 150%;text-align: left;">
<span style="font-size:12px;">Good news! We're working with <a href="https://www.linkedin.com/">LinkedIn</a>, the world's largest professional network, to make it even easier to showcase your success. That means you can now display your edX certificates on LinkedIn to show what you have learned and achieved. Just click “Add to profile” below on each of the certificates you’d like to include on LinkedIn.</span>
</td>
</tr>
</tbody></table>
</td>
</tr>
</tbody>
</table>
%for course_dict in courses_list:
<%
course_url = course_dict['course_url']
course_title = course_dict['course_title']
course_image_url = course_dict['course_image_url']
course_org = course_dict['course_org']
course_end_date = course_dict['course_end_date']
linkedin_add_url = course_dict['linkedin_add_url']
%>
## Begin table for single class
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnCaptionBlock" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody class="mcnCaptionBlockOuter">
<tr>
<td class="mcnCaptionBlockInner" valign="top" style="padding: 9px;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<table border="0" cellpadding="0" cellspacing="0" class="mcnCaptionRightContentOuter" width="100%" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody><tr>
<td valign="top" class="mcnCaptionRightContentInner" style="padding: 0 9px;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<table align="left" border="0" cellpadding="0" cellspacing="0" class="mcnCaptionRightImageContentContainer" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody><tr>
<td class="mcnCaptionRightImageContent" valign="top" style="mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<a href="${course_url}" title="" class="" target="_blank" style="word-wrap: break-word;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<img alt="${course_title}" src="${course_image_url}" width="264" style="max-width: 378px;border: 0;outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;vertical-align: bottom;" class="mcnImage">
</a>
</td>
</tr>
</tbody></table>
<table class="mcnCaptionRightTextContentContainer" align="right" border="0" cellpadding="0" cellspacing="0" width="264" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody><tr>
<td valign="top" class="mcnTextContent" style="line-height: 150%;text-align: left;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #606060;font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;font-size: 14px;">
<span style="font-size:14px;"><strong>${course_title}</strong></span><br>
<span style="font-size:12px;"><b><i>${course_org}</i></b></span><br>
<strong style="font-size: 12px;">Completed ${course_end_date}</strong><br>
<br>
<div align="right"><a href="${linkedin_add_url}" style="font-size: 12px;word-wrap: break-word;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #126f9a;font-weight: normal;text-decoration: underline;" target="_blank"><img align="none" src="http://files.edx.org/linkedin/linkedin_add_to_profile.png" width="143" height="35" alt="Add to profile"></a></div>
</td>
</tr>
</tbody></table>
</td>
</tr>
</tbody></table>
</td>
</tr>
</tbody>
</table>
## End table for single class cell
%endfor
## a really complicated hr
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnDividerBlock" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody class="mcnDividerBlockOuter">
<tr>
<td class="mcnDividerBlockInner" style="padding: 18px 18px 28px;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<table class="mcnDividerContent" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-top-width: 1px;border-top-style: solid;border-top-color: #999999;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody><tr>
<td style="mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<span></span>
</td>
</tr>
</tbody></table>
</td>
</tr>
</tbody>
</table>
## text for congrats on your accomplishment
<table border="0" cellpadding="0" cellspacing="0" width="600" id="templateBody" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;background-color: #ffffff;border-top: 0;border-bottom: 0;">
<tr>
<td valign="top" class="bodyContainer" style="mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;"><table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnTextBlock" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody class="mcnTextBlockOuter">
<tr>
<td valign="top" class="mcnTextBlockInner" style="mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<table align="left" border="0" cellpadding="0" cellspacing="0" width="600" class="mcnTextContentContainer" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody><tr>
<td valign="top" class="mcnTextContent" style="padding-top: 9px;padding-right: 18px;padding-bottom: 9px;padding-left: 18px;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #606060;font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;font-size: 14px;line-height: 150%;text-align: left;">
<span style="font-size:12px;">Congratulations on your accomplishment! Adding this to your profile will help get the word out about your impressive edX achievement. -The edX Team-</span>
</td>
</tr>
</tbody></table>
</td>
</tr>
</tbody>
</table>
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnTextBlock" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody class="mcnTextBlockOuter">
<tr>
<td valign="top" class="mcnTextBlockInner" style="mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<table align="left" border="0" cellpadding="0" cellspacing="0" width="600" class="mcnTextContentContainer" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody><tr>
<td valign="top" class="mcnTextContent" style="padding-top: 9px;padding-right: 18px;padding-bottom: 9px;padding-left: 18px;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #606060;font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;font-size: 14px;line-height: 150%;text-align: left;">
</td>
</tr>
</tbody></table>
</td>
</tr>
</tbody>
</table><table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnDividerBlock" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody class="mcnDividerBlockOuter">
<tr>
<td class="mcnDividerBlockInner" style="padding: 18px;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<table class="mcnDividerContent" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-top-width: 1px;border-top-style: solid;border-top-color: #666666;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody><tr>
<td style="mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<span></span>
</td>
</tr>
</tbody></table>
</td>
</tr>
</tbody>
</table><table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnTextBlock" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody class="mcnTextBlockOuter">
<tr>
<td valign="top" class="mcnTextBlockInner" style="mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<table align="left" border="0" cellpadding="0" cellspacing="0" width="600" class="mcnTextContentContainer" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody><tr>
<td valign="top" class="mcnTextContent" style="padding-top: 9px;padding-right: 18px;padding-bottom: 9px;padding-left: 18px;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #606060;font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;font-size: 14px;line-height: 150%;text-align: left;">
<div style="text-align: center;">
<span style="font-size:12px;">Stay connected on <a href="http://www.linkedin.com/company/edx" target="_self" style="word-wrap: break-word;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #126f9a;font-weight: normal;text-decoration: underline;">LinkedIn</a>,
<a href="https://www.facebook.com/EdxOnline" target="_self" style="word-wrap: break-word;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #126f9a;font-weight: normal;text-decoration: underline;">Facebook</a>, <a href="https://twitter.com/edXOnline" target="_self" style="word-wrap: break-word;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #126f9a;font-weight: normal;text-decoration: underline;">Twitter</a>, <a href="https://plus.google.com/b/108235383044095082735/108235383044095082735" target="_self" style="word-wrap: break-word;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #126f9a;font-weight: normal;text-decoration: underline;">Google+</a> and more for news and updates.</span></div>
</td>
</tr>
</tbody></table>
</td>
</tr>
</tbody>
</table><table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnDividerBlock" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody class="mcnDividerBlockOuter">
<tr>
<td class="mcnDividerBlockInner" style="padding: 18px 18px 3px;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<table class="mcnDividerContent" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-top-width: 1px;border-top-style: solid;border-top-color: #666666;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody><tr>
<td style="mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<span></span>
</td>
</tr>
</tbody></table>
</td>
</tr>
</tbody>
</table><table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnTextBlock" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody class="mcnTextBlockOuter">
<tr>
<td valign="top" class="mcnTextBlockInner" style="mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<table align="left" border="0" cellpadding="0" cellspacing="0" width="600" class="mcnTextContentContainer" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody><tr>
<td valign="top" class="mcnTextContent" style="padding-top: 9px;padding-right: 18px;padding-bottom: 9px;padding-left: 18px;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #606060;font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;font-size: 14px;line-height: 150%;text-align: left;">
<div style="text-align: right;">
<a href="http://facebook.com/edxonline" target="_blank" style="word-wrap: break-word;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #126f9a;font-weight: normal;text-decoration: underline;"><img align="none" height="16" src="http://gallery.mailchimp.com/1822a33c054dc20e223ca40e2/images/social_facebook.png" style="width: 16px;height: 16px;border: 0;outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;" width="16"></a>&nbsp;&nbsp;<a href="http://twitter.com/edxonline" target="_blank" style="word-wrap: break-word;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #126f9a;font-weight: normal;text-decoration: underline;"><img align="none" height="16" src="http://gallery.mailchimp.com/1822a33c054dc20e223ca40e2/images/social_twitter.png" style="width: 16px;height: 16px;border: 0;outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;" width="16"></a>&nbsp;&nbsp;<a href="https://plus.google.com/108235383044095082735" target="_blank" style="word-wrap: break-word;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #126f9a;font-weight: normal;text-decoration: underline;"><img align="none" height="16" src="http://gallery.mailchimp.com/1822a33c054dc20e223ca40e2/images/social_googleplus.png" style="width: 16px;height: 16px;border: 0;outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;" width="16"></a>&nbsp;&nbsp;<a href="http://www.linkedin.com/company/edx?trk=nav_account_sub_nav_company_admin" target="_blank" style="word-wrap: break-word;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #126f9a;font-weight: normal;text-decoration: underline;"><img align="none" height="16" src="http://gallery.mailchimp.com/1822a33c054dc20e223ca40e2/images/social_linkedin.png" style="width: 16px;height: 16px;border: 0;outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;" width="16"></a>&nbsp;&nbsp;<a href="http://vk.com/edxrussia" target="_blank" style="word-wrap: break-word;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #126f9a;font-weight: normal;text-decoration: underline;"><img align="none" height="16" src="http://gallery.mailchimp.com/1822a33c054dc20e223ca40e2/images/social_vkontakte.png" style="width: 16px;height: 16px;border: 0;outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;" width="16"></a>&nbsp;&nbsp;<a href="http://www.meetup.com/edX-Communities/" target="_blank" style="word-wrap: break-word;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #126f9a;font-weight: normal;text-decoration: underline;"><img align="none" height="16" src="http://gallery.mailchimp.com/1822a33c054dc20e223ca40e2/images/social_meetup.png" style="width: 16px;height: 16px;border: 0;outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;" width="16"></a></div>
</td>
</tr>
</tbody></table>
</td>
</tr>
</tbody>
</table></td>
</tr>
</table>
## // END BODY
</td>
</tr>
<tr>
<td align="center" valign="top" style="mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
## BEGIN FOOTER //
<table border="0" cellpadding="0" cellspacing="0" width="600" id="templateFooter" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;background-color: #0f6a93;border-top: 0;border-bottom: 0;">
<tr>
<td valign="top" class="footerContainer" style="padding-bottom: 9px;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;"><table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnTextBlock" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody class="mcnTextBlockOuter">
<tr>
<td valign="top" class="mcnTextBlockInner" style="mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<table align="left" border="0" cellpadding="0" cellspacing="0" width="600" class="mcnTextContentContainer" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody><tr>
<td valign="top" class="mcnTextContent" style="padding-top: 9px;padding-right: 18px;padding-bottom: 9px;padding-left: 18px;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #f2f2f2;font-family: Helvetica;font-size: 11px;line-height: 125%;text-align: left;">
<img src="http://files.edx.org/linkedin/linkedin-email-0.gif" border=0>
<br>
<em>Copyright © 2014 edX, All rights reserved.</em><br>
<br>
<b>Our mailing address is:</b><br>
edX<br>
11 Cambridge Center, Suite 101<br>
Cambridge, MA, USA 02142<br>
<br>
</td>
</tr>
</tbody></table>
</td>
</tr>
</tbody>
</table></td>
</tr>
</table>
## // END FOOTER
</td>
</tr>
</table>
## // END TEMPLATE
</td>
</tr>
</table>
</center>
</body>
</html>
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