diff --git a/common/static/sass/edx-pattern-library-shims/_buttons.scss b/common/static/sass/edx-pattern-library-shims/_buttons.scss
index cb5e5d85dc4ccd369a68ebf51cb70c10e51caf1a..d0eaf87b925b3a2ea0a0eba68392fde10874fc2b 100644
--- a/common/static/sass/edx-pattern-library-shims/_buttons.scss
+++ b/common/static/sass/edx-pattern-library-shims/_buttons.scss
@@ -121,3 +121,32 @@
     color: $btn-brand-disabled-color;
   }
 }
+
+// ----------------------------
+// #UPGRADE
+// ----------------------------
+.btn-upgrade {
+  @extend %btn-shims;
+
+  border-color: $btn-upgrade-border-color;
+  background: $btn-upgrade-background;
+  color: $btn-upgrade-color;
+
+  // STATE: hover and focus
+  &:hover,
+  &.is-hovered,
+  &:focus,
+  &.is-focused {
+    border-color: $btn-upgrade-focus-border-color;
+    background-color: $btn-upgrade-focus-background;
+    color: $btn-upgrade-focus-color;
+  }
+
+  // STATE: is disabled
+  &:disabled,
+  &.is-disabled {
+    border-color: $btn-disabled-border-color;
+    background: $btn-brand-disabled-background;
+    color: $btn-upgrade-color;
+  }
+}
diff --git a/common/static/sass/edx-pattern-library-shims/base/_variables.scss b/common/static/sass/edx-pattern-library-shims/base/_variables.scss
index 3ae121632df9ac0bb452c09dc799589ea39b9c07..dba1a33ad579c508c7bab6a0ea1dd0f0e3f068da 100644
--- a/common/static/sass/edx-pattern-library-shims/base/_variables.scss
+++ b/common/static/sass/edx-pattern-library-shims/base/_variables.scss
@@ -143,9 +143,8 @@ $error-color: rgb(203, 7, 18) !default;
 $success-color: rgb(0, 155, 0) !default;
 $warning-color: rgb(255, 192, 31) !default;
 $warning-color-accent: rgb(255, 252, 221) !default;
-$general-color: $uxpl-blue-base !default;;
-$general-color-accent: $uxpl-blue-base !default
-
+$general-color: $uxpl-blue-base !default;
+$general-color-accent: $uxpl-blue-base !default;
 
 // CAPA correctness color to be consistent with Alert styles above
 $correct: $success-color !default;
@@ -181,6 +180,16 @@ $btn-brand-active-background:             $uxpl-blue-base !default;
 $btn-brand-disabled-background:           #f2f3f3 !default;
 $btn-brand-disabled-color:                #676666 !default;
 
+// Upgrade button
+$btn-upgrade-border-color:                $uxpl-green-base !default;
+$btn-upgrade-background:                  $uxpl-green-base !default;
+$btn-upgrade-color:                       #fcfcfc !default;
+$btn-upgrade-focus-color:                 $btn-upgrade-color !default;
+$btn-upgrade-focus-border-color:          rgb(0, 155, 0) !default;
+$btn-upgrade-focus-background:            rgb(0, 155, 0) !default;
+$btn-upgrade-active-border-color:         $uxpl-green-base !default;
+$btn-upgrade-active-background:           $uxpl-green-base !default;
+
 // ----------------------------
 // #SETTINGS
 // ----------------------------
diff --git a/lms/djangoapps/commerce/utils.py b/lms/djangoapps/commerce/utils.py
index 7de0e6d6f1f155e30b5354edeeb82207932c7d03..daf83e4d0b03f77777ec439a81a32787784a2dfe 100644
--- a/lms/djangoapps/commerce/utils.py
+++ b/lms/djangoapps/commerce/utils.py
@@ -4,6 +4,8 @@ from urlparse import urljoin
 
 import waffle
 from django.conf import settings
+from django.core.urlresolvers import reverse
+from student.models import CourseEnrollment
 
 from commerce.models import CommerceConfiguration
 from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
@@ -93,3 +95,16 @@ class EcommerceService(object):
             checkout_page_path=self.get_absolute_ecommerce_url(self.config.MULTIPLE_ITEMS_BASKET_PAGE_URL),
             skus=urlencode({'sku': skus}, doseq=True),
         )
+
+    def upgrade_url(self, user, course_key):
+        """
+        Returns the URL for the user to upgrade, or None if not applicable.
+        """
+        enrollment = CourseEnrollment.get_enrollment(user, course_key)
+        verified_mode = enrollment.verified_mode if enrollment else None
+        if verified_mode:
+            if self.is_enabled(user):
+                return self.get_checkout_page_url(verified_mode.sku)
+            else:
+                return reverse('verify_student_upgrade_and_verify', args=(course_key,))
+        return None
diff --git a/lms/djangoapps/courseware/date_summary.py b/lms/djangoapps/courseware/date_summary.py
index d27bb188efced344c492d2dbd7f4d3e07776f1e7..205edbe3a858a309c2cd2191a554fd32ba1bdaac 100644
--- a/lms/djangoapps/courseware/date_summary.py
+++ b/lms/djangoapps/courseware/date_summary.py
@@ -3,26 +3,45 @@ This module provides date summary blocks for the Course Info
 page. Each block gives information about a particular
 course-run-specific date which will be displayed to the user.
 """
+import crum
 import datetime
 
 from babel.dates import format_timedelta
+
+from django.conf import settings
 from django.core.urlresolvers import reverse
 from django.utils.functional import cached_property
 from django.utils.translation import get_language, to_locale, ugettext_lazy
 from django.utils.translation import ugettext as _
 from lazy import lazy
-from pytz import timezone, utc
+from pytz import utc
 
-from course_modes.models import CourseMode
+from course_modes.models import CourseMode, get_cosmetic_verified_display_price
 from lms.djangoapps.commerce.utils import EcommerceService
 from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification, VerificationDeadline
 from openedx.core.djangoapps.certificates.api import can_show_certificate_available_date_field
+from openedx.core.djangolib.markup import HTML, Text
+from openedx.features.course_experience import CourseHomeMessages, UPGRADE_DEADLINE_MESSAGE
 from student.models import CourseEnrollment
 
+from .context_processor import user_timezone_locale_prefs
+
 
 class DateSummary(object):
     """Base class for all date summary blocks."""
 
+    # A consistent representation of the current time.
+    _current_time = None
+
+    @property
+    def current_time(self):
+        """
+        Returns a consistent current time.
+        """
+        if self._current_time is None:
+            self._current_time = datetime.datetime.now(utc)
+        return self._current_time
+
     @property
     def css_class(self):
         """
@@ -41,6 +60,12 @@ class DateSummary(object):
         """The detail text displayed by this summary."""
         return ''
 
+    def register_alerts(self, request, course):
+        """
+        Registers any relevant course alerts given the current request.
+        """
+        pass
+
     @property
     def date(self):
         """This summary's date."""
@@ -64,15 +89,6 @@ class DateSummary(object):
         """The text of the link."""
         return ''
 
-    @property
-    def time_zone(self):
-        """
-        The time zone in which to display -- defaults to UTC
-        """
-        return timezone(
-            self.user.preferences.model.get_value(self.user, "time_zone", "UTC")
-        )
-
     def __init__(self, course, user, course_id=None):
         self.course = course
         self.user = user
@@ -87,7 +103,7 @@ class DateSummary(object):
         if self.date is None:
             return ''
         locale = to_locale(get_language())
-        delta = self.date - datetime.datetime.now(utc)
+        delta = self.date - self.current_time
         try:
             relative_date = format_timedelta(delta, locale=locale)
         # Babel doesn't have translations for Esperanto, so we get
@@ -117,7 +133,7 @@ class DateSummary(object):
         future.
         """
         if self.date is not None:
-            return datetime.datetime.now(utc).date() <= self.date.date()
+            return self.current_time.date() <= self.date.date()
         return False
 
     def deadline_has_passed(self):
@@ -126,7 +142,52 @@ class DateSummary(object):
         Returns False otherwise.
         """
         deadline = self.date
-        return deadline is not None and deadline <= datetime.datetime.now(utc)
+        return deadline is not None and deadline <= self.current_time
+
+    @property
+    def time_remaining_string(self):
+        """
+        Returns the time remaining as a localized string.
+        """
+        locale = to_locale(get_language())
+        return format_timedelta(self.date - self.current_time, locale=locale)
+
+    def date_html(self, date_format='shortDate'):
+        """
+        Returns a representation of the date as HTML.
+
+        Note: this returns a span that will be localized on the client.
+        """
+        locale = to_locale(get_language())
+        user_timezone = user_timezone_locale_prefs(crum.get_current_request())['user_timezone']
+        return HTML(
+            '<span class="date localized-datetime" data-format="{date_format}" data-datetime="{date_time}"'
+            ' data-timezone="{user_timezone}" data-language="{user_language}">'
+            '</span>'
+        ).format(
+            date_format=date_format,
+            date_time=self.date,
+            user_timezone=user_timezone,
+            user_language=locale,
+        )
+
+    @property
+    def long_date_html(self):
+        """
+        Returns a long representation of the date as HTML.
+
+        Note: this returns a span that will be localized on the client.
+        """
+        return self.date_html(date_format='shortDate')
+
+    @property
+    def short_time_html(self):
+        """
+        Returns a short representation of the time as HTML.
+
+        Note: this returns a span that will be localized on the client.
+        """
+        return self.date_html(date_format='shortTime')
 
     def __repr__(self):
         return u'DateSummary: "{title}" {date} is_enabled={is_enabled}'.format(
@@ -151,7 +212,7 @@ class TodaysDate(DateSummary):
 
     @property
     def date(self):
-        return datetime.datetime.now(utc)
+        return self.current_time
 
     @property
     def title(self):
@@ -169,6 +230,35 @@ class CourseStartDate(DateSummary):
     def date(self):
         return self.course.start
 
+    def register_alerts(self, request, course):
+        """
+        Registers an alert if the course has not started yet.
+        """
+        is_enrolled = CourseEnrollment.get_enrollment(request.user, course.id)
+        if not course.start or not is_enrolled:
+            return
+        days_until_start = (course.start - self.current_time).days
+        if course.start > self.current_time:
+            if days_until_start > 0:
+                CourseHomeMessages.register_info_message(
+                    request,
+                    Text(_(
+                        "Don't forget to add a calendar reminder!"
+                    )),
+                    title=Text(_("Course starts in {time_remaining_string} on {course_start_date}.")).format(
+                        time_remaining_string=self.time_remaining_string,
+                        course_start_date=self.long_date_html,
+                    )
+                )
+            else:
+                CourseHomeMessages.register_info_message(
+                    request,
+                    Text(_("Course starts in {time_remaining_string} at {course_start_time}.")).format(
+                        time_remaining_string=self.time_remaining_string,
+                        course_start_time=self.short_time_html,
+                    )
+                )
+
 
 class CourseEndDate(DateSummary):
     """
@@ -183,7 +273,7 @@ class CourseEndDate(DateSummary):
 
     @property
     def description(self):
-        if datetime.datetime.now(utc) <= self.date:
+        if self.current_time <= self.date:
             mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course_id)
             if is_active and CourseMode.is_eligible_for_certificate(mode):
                 return _('To earn a certificate, you must complete all requirements before this date.')
@@ -195,6 +285,35 @@ class CourseEndDate(DateSummary):
     def date(self):
         return self.course.end
 
+    def register_alerts(self, request, course):
+        """
+        Registers an alert if the end date is approaching.
+        """
+        is_enrolled = CourseEnrollment.get_enrollment(request.user, course.id)
+        if not course.start or self.current_time < course.start or not is_enrolled:
+            return
+        days_until_end = (course.end - self.current_time).days
+        if course.end > self.current_time and days_until_end <= settings.COURSE_MESSAGE_ALERT_DURATION_IN_DAYS:
+            if days_until_end > 0:
+                CourseHomeMessages.register_info_message(
+                    request,
+                    Text(self.description),
+                    title=Text(_('This course is ending in {time_remaining_string} on {course_end_date}.')).format(
+                        time_remaining_string=self.time_remaining_string,
+                        course_end_date=self.long_date_html,
+                    )
+                )
+            else:
+                CourseHomeMessages.register_info_message(
+                    request,
+                    Text(self.description),
+                    title=Text(_('This course is ending in {time_remaining_string} at {course_end_time}.')).format(
+                        time_remaining_string=self.time_remaining_string,
+                        course_end_time=self.short_time_html,
+                    )
+                )
+
+
 
 class CertificateAvailableDate(DateSummary):
     """
@@ -216,7 +335,7 @@ class CertificateAvailableDate(DateSummary):
             can_show_certificate_available_date_field(self.course) and
             self.has_certificate_modes and
             self.date is not None and
-            datetime.datetime.now(utc) <= self.date and
+            self.current_time <= self.date and
             len(self.active_certificates) > 0
         )
 
@@ -252,13 +371,7 @@ class VerifiedUpgradeDeadlineDate(DateSummary):
 
     @property
     def link(self):
-        ecommerce_service = EcommerceService()
-        if ecommerce_service.is_enabled(self.user):
-            course_mode = CourseMode.objects.get(
-                course_id=self.course_id, mode_slug=CourseMode.VERIFIED
-            )
-            return ecommerce_service.get_checkout_page_url(course_mode.sku)
-        return reverse('verify_student_upgrade_and_verify', args=(self.course_id,))
+        return EcommerceService().upgrade_url(self.user, self.course_id)
 
     @cached_property
     def enrollment(self):
@@ -299,6 +412,39 @@ class VerifiedUpgradeDeadlineDate(DateSummary):
 
         return deadline
 
+    def register_alerts(self, request, course):
+        """
+        Registers an alert if the verification deadline is approaching.
+        """
+        upgrade_price = get_cosmetic_verified_display_price(course)
+        if not UPGRADE_DEADLINE_MESSAGE.is_enabled(course.id) or not self.is_enabled or not upgrade_price:
+            return
+        days_left_to_upgrade = (self.date - self.current_time).days
+        if self.date > self.current_time and days_left_to_upgrade <= settings.COURSE_MESSAGE_ALERT_DURATION_IN_DAYS:
+            CourseHomeMessages.register_info_message(
+                request,
+                Text(_(
+                    'In order to qualify for a certificate, you must meet all course grading '
+                    'requirements, upgrade before the course deadline, and successfully verify '
+                    'your identity on {platform_name} if you have not done so already.{button_panel}'
+                )).format(
+                    platform_name=settings.PLATFORM_NAME,
+                    button_panel=HTML(
+                        '<div class="message-actions">'
+                        '<a class="btn btn-upgrade" href="{upgrade_url}">{upgrade_label}</a>'
+                        '</div>'
+                    ).format(
+                        upgrade_url=self.link,
+                        upgrade_label=Text(_('Upgrade ({upgrade_price})')).format(upgrade_price=upgrade_price),
+                    )
+                ),
+                title=Text(_(
+                    "Don't forget, you have {time_remaining_string} left to upgrade to a Verified Certificate."
+                )).format(
+                    time_remaining_string=self.time_remaining_string,
+                )
+            )
+
 
 class VerificationDeadlineDate(DateSummary):
     """
diff --git a/lms/djangoapps/courseware/tests/test_date_summary.py b/lms/djangoapps/courseware/tests/test_date_summary.py
index 069edb6f2b9c0985f1d6449d8fb99c2308e39fdf..08c2f320e61c208f74ad830c2772a97e19830c0b 100644
--- a/lms/djangoapps/courseware/tests/test_date_summary.py
+++ b/lms/djangoapps/courseware/tests/test_date_summary.py
@@ -4,7 +4,9 @@ from datetime import datetime, timedelta
 
 import ddt
 import waffle
+from django.contrib.messages.middleware import MessageMiddleware
 from django.core.urlresolvers import reverse
+from django.test import RequestFactory, TestCase
 from freezegun import freeze_time
 from mock import patch
 from nose.plugins.attrib import attr
@@ -31,7 +33,7 @@ from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
 from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory
 from openedx.core.djangoapps.user_api.preferences.api import set_user_preference
 from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag
-from openedx.features.course_experience import UNIFIED_COURSE_TAB_FLAG
+from openedx.features.course_experience import CourseHomeMessages, UNIFIED_COURSE_TAB_FLAG, UPGRADE_DEADLINE_MESSAGE
 from student.tests.factories import CourseEnrollmentFactory, UserFactory, TEST_PASSWORD
 from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory
@@ -46,20 +48,6 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
         super(CourseDateSummaryTest, self).setUp()
         SelfPacedConfiguration.objects.create(enable_course_home_improvements=True)
 
-    def create_user(self, verification_status=None):
-        """ Create a new User instance.
-
-        Arguments:
-            verification_status (str): User's verification status. If this value is set an instance of
-                SoftwareSecurePhotoVerification will be created for the user with the specified status.
-        """
-        user = UserFactory()
-
-        if verification_status is not None:
-            SoftwareSecurePhotoVerificationFactory.create(user=user, status=verification_status)
-
-        return user
-
     def enable_course_certificates(self, course):
         """ Enable course certificate configuration """
         course.certificates = {
@@ -74,7 +62,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
     def test_course_info_feature_flag(self):
         SelfPacedConfiguration(enable_course_home_improvements=False).save()
         course = create_course_run()
-        user = self.create_user()
+        user = create_user()
         CourseEnrollmentFactory(course_id=course.id, user=user, mode=CourseMode.VERIFIED)
 
         self.client.login(username=user.username, password=TEST_PASSWORD)
@@ -144,7 +132,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
     @ddt.unpack
     def test_enabled_block_types(self, course_kwargs, user_kwargs, expected_blocks):
         course = create_course_run(**course_kwargs)
-        user = self.create_user(**user_kwargs)
+        user = create_user(**user_kwargs)
         CourseEnrollmentFactory(course_id=course.id, user=user, mode=CourseMode.VERIFIED)
         self.assert_block_types(course, user, expected_blocks)
 
@@ -160,12 +148,12 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
     @ddt.unpack
     def test_enabled_block_types_without_enrollment(self, course_kwargs, expected_blocks):
         course = create_course_run(**course_kwargs)
-        user = self.create_user()
+        user = create_user()
         self.assert_block_types(course, user, expected_blocks)
 
     def test_enabled_block_types_with_non_upgradeable_course_run(self):
         course = create_course_run(days_till_start=-10, days_till_verification_deadline=None)
-        user = self.create_user()
+        user = create_user()
         CourseMode.objects.get(course_id=course.id, mode_slug=CourseMode.VERIFIED).delete()
         CourseEnrollmentFactory(course_id=course.id, user=user, mode=CourseMode.AUDIT)
         self.assert_block_types(course, user, (TodaysDate, CourseEndDate))
@@ -177,7 +165,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
         """
         with freeze_time('2015-01-02'):
             course = create_course_run()
-            user = self.create_user()
+            user = create_user()
             block = TodaysDate(course, user)
             self.assertTrue(block.is_enabled)
             self.assertEqual(block.date, datetime.now(utc))
@@ -191,7 +179,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
     def test_todays_date_no_timezone(self, url_name):
         with freeze_time('2015-01-02'):
             course = create_course_run()
-            user = self.create_user()
+            user = create_user()
             self.client.login(username=user.username, password=TEST_PASSWORD)
 
             html_elements = [
@@ -216,7 +204,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
     def test_todays_date_timezone(self, url_name):
         with freeze_time('2015-01-02'):
             course = create_course_run()
-            user = self.create_user()
+            user = create_user()
             self.client.login(username=user.username, password=TEST_PASSWORD)
             set_user_preference(user, 'time_zone', 'America/Los_Angeles')
             url = reverse(url_name, args=(course.id,))
@@ -237,7 +225,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
     ## Tests Course Start Date
     def test_course_start_date(self):
         course = create_course_run()
-        user = self.create_user()
+        user = create_user()
         block = CourseStartDate(course, user)
         self.assertEqual(block.date, course.start)
 
@@ -249,7 +237,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
     def test_start_date_render(self, url_name):
         with freeze_time('2015-01-02'):
             course = create_course_run()
-            user = self.create_user()
+            user = create_user()
             self.client.login(username=user.username, password=TEST_PASSWORD)
             url = reverse(url_name, args=(course.id,))
             response = self.client.get(url, follow=True)
@@ -268,7 +256,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
     def test_start_date_render_time_zone(self, url_name):
         with freeze_time('2015-01-02'):
             course = create_course_run()
-            user = self.create_user()
+            user = create_user()
             self.client.login(username=user.username, password=TEST_PASSWORD)
             set_user_preference(user, 'time_zone', 'America/Los_Angeles')
             url = reverse(url_name, args=(course.id,))
@@ -284,7 +272,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
     ## Tests Course End Date Block
     def test_course_end_date_for_certificate_eligible_mode(self):
         course = create_course_run(days_till_start=-1)
-        user = self.create_user()
+        user = create_user()
         CourseEnrollmentFactory(course_id=course.id, user=user, mode=CourseMode.VERIFIED)
         block = CourseEndDate(course, user)
         self.assertEqual(
@@ -294,7 +282,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
 
     def test_course_end_date_for_non_certificate_eligible_mode(self):
         course = create_course_run(days_till_start=-1)
-        user = self.create_user()
+        user = create_user()
         CourseEnrollmentFactory(course_id=course.id, user=user, mode=CourseMode.AUDIT)
         block = CourseEndDate(course, user)
         self.assertEqual(
@@ -305,7 +293,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
 
     def test_course_end_date_after_course(self):
         course = create_course_run(days_till_start=-2, days_till_end=-1)
-        user = self.create_user()
+        user = create_user()
         CourseEnrollmentFactory(course_id=course.id, user=user, mode=CourseMode.VERIFIED)
         block = CourseEndDate(course, user)
         self.assertEqual(
@@ -319,7 +307,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
         sku = 'TESTSKU'
         configuration = CommerceConfiguration.objects.create(checkout_on_ecommerce_service=True)
         course = create_course_run()
-        user = self.create_user()
+        user = create_user()
         course_mode = CourseMode.objects.get(course_id=course.id, mode_slug=CourseMode.VERIFIED)
         course_mode.sku = sku
         course_mode.save()
@@ -332,7 +320,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
     @waffle.testutils.override_switch('certificates.auto_certificate_generation', True)
     def test_no_certificate_available_date(self):
         course = create_course_run(days_till_start=-1)
-        user = self.create_user()
+        user = create_user()
         CourseEnrollmentFactory(course_id=course.id, user=user, mode=CourseMode.AUDIT)
         block = CertificateAvailableDate(course, user)
         self.assertEqual(block.date, None)
@@ -342,7 +330,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
     @waffle.testutils.override_switch('certificates.auto_certificate_generation', True)
     def test_no_certificate_available_date_for_self_paced(self):
         course = create_self_paced_course_run()
-        verified_user = self.create_user()
+        verified_user = create_user()
         CourseEnrollmentFactory(course_id=course.id, user=verified_user, mode=CourseMode.VERIFIED)
         course.certificate_available_date = datetime.now(utc) + timedelta(days=7)
         course.save()
@@ -356,7 +344,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
         if the course only has audit mode.
         """
         course = create_course_run()
-        audit_user = self.create_user()
+        audit_user = create_user()
 
         # Enroll learner in the audit mode and verify the course only has 1 mode (audit)
         CourseEnrollmentFactory(course_id=course.id, user=audit_user, mode=CourseMode.AUDIT)
@@ -376,9 +364,9 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
     @waffle.testutils.override_switch('certificates.auto_certificate_generation', True)
     def test_certificate_available_date_defined(self):
         course = create_course_run()
-        audit_user = self.create_user()
+        audit_user = create_user()
         CourseEnrollmentFactory(course_id=course.id, user=audit_user, mode=CourseMode.AUDIT)
-        verified_user = self.create_user()
+        verified_user = create_user()
         CourseEnrollmentFactory(course_id=course.id, user=verified_user, mode=CourseMode.VERIFIED)
         course.certificate_available_date = datetime.now(utc) + timedelta(days=7)
         self.enable_course_certificates(course)
@@ -391,14 +379,14 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
     ## VerificationDeadlineDate
     def test_no_verification_deadline(self):
         course = create_course_run(days_till_start=-1, days_till_verification_deadline=None)
-        user = self.create_user()
+        user = create_user()
         CourseEnrollmentFactory(course_id=course.id, user=user, mode=CourseMode.VERIFIED)
         block = VerificationDeadlineDate(course, user)
         self.assertFalse(block.is_enabled)
 
     def test_no_verified_enrollment(self):
         course = create_course_run(days_till_start=-1)
-        user = self.create_user()
+        user = create_user()
         CourseEnrollmentFactory(course_id=course.id, user=user, mode=CourseMode.AUDIT)
         block = VerificationDeadlineDate(course, user)
         self.assertFalse(block.is_enabled)
@@ -406,7 +394,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
     def test_verification_deadline_date_upcoming(self):
         with freeze_time('2015-01-02'):
             course = create_course_run(days_till_start=-1)
-            user = self.create_user()
+            user = create_user()
             CourseEnrollmentFactory(course_id=course.id, user=user, mode=CourseMode.VERIFIED)
 
             block = VerificationDeadlineDate(course, user)
@@ -423,7 +411,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
     def test_verification_deadline_date_retry(self):
         with freeze_time('2015-01-02'):
             course = create_course_run(days_till_start=-1)
-            user = self.create_user(verification_status='denied')
+            user = create_user(verification_status='denied')
             CourseEnrollmentFactory(course_id=course.id, user=user, mode=CourseMode.VERIFIED)
 
             block = VerificationDeadlineDate(course, user)
@@ -440,7 +428,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
     def test_verification_deadline_date_denied(self):
         with freeze_time('2015-01-02'):
             course = create_course_run(days_till_start=-10, days_till_verification_deadline=-1)
-            user = self.create_user(verification_status='denied')
+            user = create_user(verification_status='denied')
             CourseEnrollmentFactory(course_id=course.id, user=user, mode=CourseMode.VERIFIED)
 
             block = VerificationDeadlineDate(course, user)
@@ -462,13 +450,104 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
     def test_render_date_string_past(self, delta, expected_date_string):
         with freeze_time('2015-01-02'):
             course = create_course_run(days_till_start=-10, days_till_verification_deadline=delta)
-            user = self.create_user(verification_status='denied')
+            user = create_user(verification_status='denied')
             CourseEnrollmentFactory(course_id=course.id, user=user, mode=CourseMode.VERIFIED)
 
             block = VerificationDeadlineDate(course, user)
             self.assertEqual(block.relative_datestring, expected_date_string)
 
 
+@attr(shard=1)
+@ddt.ddt
+class TestDateAlerts(SharedModuleStoreTestCase):
+    """
+    Unit tests for date alerts.
+    """
+    def setUp(self):
+        super(TestDateAlerts, self).setUp()
+        with freeze_time('2017-07-01 09:00:00'):
+            self.course = create_course_run(days_till_start=0)
+            self.enrollment = CourseEnrollmentFactory(course_id=self.course.id, mode=CourseMode.AUDIT)
+            self.request = RequestFactory().request()
+            self.request.session = {}
+            self.request.user = self.enrollment.user
+            MessageMiddleware().process_request(self.request)
+
+    @ddt.data(
+        ['2017-01-01 09:00:00', u'in 6 months on <span class="date localized-datetime" data-format="shortDate"'],
+        ['2017-06-17 09:00:00', u'in 2 weeks on <span class="date localized-datetime" data-format="shortDate"'],
+        ['2017-06-30 10:00:00', u'in 1 day at <span class="date localized-datetime" data-format="shortTime"'],
+        ['2017-07-01 08:00:00', u'in 1 hour at <span class="date localized-datetime" data-format="shortTime"'],
+        ['2017-07-01 08:55:00', u'in 5 minutes at <span class="date localized-datetime" data-format="shortTime"'],
+        ['2017-07-01 09:00:00', None],
+        ['2017-08-01 09:00:00', None],
+    )
+    @ddt.unpack
+    def test_start_date_alert(self, current_time, expected_message_html):
+        """
+        Verify that course start date alerts are registered.
+        """
+        with freeze_time(current_time):
+            block = CourseStartDate(self.course, self.request.user)
+            block.register_alerts(self.request, self.course)
+            messages = list(CourseHomeMessages.user_messages(self.request))
+            if expected_message_html:
+                self.assertEqual(len(messages), 1)
+                self.assertIn(expected_message_html, messages[0].message_html)
+            else:
+                self.assertEqual(len(messages), 0)
+
+    @ddt.data(
+        ['2017-06-30 09:00:00', None],
+        ['2017-07-01 09:00:00', u'in 2 weeks on <span class="date localized-datetime" data-format="shortDate"'],
+        ['2017-07-14 10:00:00', u'in 1 day at <span class="date localized-datetime" data-format="shortTime"'],
+        ['2017-07-15 08:00:00', u'in 1 hour at <span class="date localized-datetime" data-format="shortTime"'],
+        ['2017-07-15 08:55:00', u'in 5 minutes at <span class="date localized-datetime" data-format="shortTime"'],
+        ['2017-07-15 09:00:00', None],
+        ['2017-08-15 09:00:00', None],
+    )
+    @ddt.unpack
+    def test_end_date_alert(self, current_time, expected_message_html):
+        """
+        Verify that course end date alerts are registered.
+        """
+        with freeze_time(current_time):
+            block = CourseEndDate(self.course, self.request.user)
+            block.register_alerts(self.request, self.course)
+            messages = list(CourseHomeMessages.user_messages(self.request))
+            if expected_message_html:
+                self.assertEqual(len(messages), 1)
+                self.assertIn(expected_message_html, messages[0].message_html)
+            else:
+                self.assertEqual(len(messages), 0)
+
+    @ddt.data(
+        ['2017-06-20 09:00:00', None],
+        ['2017-06-21 09:00:00', u'Don&#39;t forget, you have 2 weeks left to upgrade to a Verified Certificate.'],
+        ['2017-07-04 10:00:00', u'Don&#39;t forget, you have 1 day left to upgrade to a Verified Certificate.'],
+        ['2017-07-05 08:00:00', u'Don&#39;t forget, you have 1 hour left to upgrade to a Verified Certificate.'],
+        ['2017-07-05 08:55:00', u'Don&#39;t forget, you have 5 minutes left to upgrade to a Verified Certificate.'],
+        ['2017-07-05 09:00:00', None],
+        ['2017-08-05 09:00:00', None],
+    )
+    @ddt.unpack
+    @override_waffle_flag(UPGRADE_DEADLINE_MESSAGE, active=True)
+    def test_verified_upgrade_deadline_alert(self, current_time, expected_message_html):
+        """
+        Verify the verified upgrade deadline alerts.
+        """
+        with freeze_time(current_time):
+            block = VerifiedUpgradeDeadlineDate(self.course, self.request.user)
+            block.register_alerts(self.request, self.course)
+            messages = list(CourseHomeMessages.user_messages(self.request))
+            if expected_message_html:
+                self.assertEqual(len(messages), 1)
+                self.assertIn(expected_message_html, messages[0].message_html)
+            else:
+                self.assertEqual(len(messages), 0)
+
+
+
 @attr(shard=1)
 class TestScheduleOverrides(SharedModuleStoreTestCase):
 
@@ -560,6 +639,21 @@ class TestScheduleOverrides(SharedModuleStoreTestCase):
         self.assertEqual(block.date, expected)
 
 
+def create_user(verification_status=None):
+    """ Create a new User instance.
+
+    Arguments:
+        verification_status (str): User's verification status. If this value is set an instance of
+            SoftwareSecurePhotoVerification will be created for the user with the specified status.
+    """
+    user = UserFactory()
+
+    if verification_status is not None:
+        SoftwareSecurePhotoVerificationFactory.create(user=user, status=verification_status)
+
+    return user
+
+
 def create_course_run(
     days_till_start=1, days_till_end=14, days_till_upgrade_deadline=4, days_till_verification_deadline=14,
 ):
diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py
index 1b772ae3efc0f2247e05fbf72adaf7de808ed655..a200dfc22df65fb5e04730972d25ae04f923b52f 100644
--- a/lms/djangoapps/courseware/tests/test_views.py
+++ b/lms/djangoapps/courseware/tests/test_views.py
@@ -213,8 +213,8 @@ class IndexQueryTestCase(ModuleStoreTestCase):
     NUM_PROBLEMS = 20
 
     @ddt.data(
-        (ModuleStoreEnum.Type.mongo, 10, 145),
-        (ModuleStoreEnum.Type.split, 4, 145),
+        (ModuleStoreEnum.Type.mongo, 10, 147),
+        (ModuleStoreEnum.Type.split, 4, 147),
     )
     @ddt.unpack
     def test_index_query_counts(self, store_type, expected_mongo_query_count, expected_mysql_query_count):
diff --git a/lms/envs/common.py b/lms/envs/common.py
index b9a8ab1af1e5a0c73b7e01a8db9df5d0a7b1fbf3..6b561a591f9ff82c33788d9a447180cb6b1e2e5a 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -431,6 +431,9 @@ XQUEUE_WAITTIME_BETWEEN_REQUESTS = 5  # seconds
 RETRY_ACTIVATION_EMAIL_MAX_ATTEMPTS = 5
 RETRY_ACTIVATION_EMAIL_TIMEOUT = 0.5
 
+# Deadline message configurations
+COURSE_MESSAGE_ALERT_DURATION_IN_DAYS = 14
+
 ############################# SET PATH INFORMATION #############################
 PROJECT_ROOT = path(__file__).abspath().dirname().dirname()  # /edx-platform/lms
 REPO_ROOT = PROJECT_ROOT.dirname()
@@ -2589,6 +2592,7 @@ MAX_FAILED_LOGIN_ATTEMPTS_LOCKOUT_PERIOD_SECS = 15 * 60
 TIME_ZONE_DISPLAYED_FOR_DEADLINES = 'UTC'
 
 
+
 ########################## VIDEO IMAGE STORAGE ############################
 
 VIDEO_IMAGE_SETTINGS = dict(
diff --git a/lms/static/sass/_build-base-v1.scss b/lms/static/sass/_build-base-v1.scss
index 0c63cb6c344c7cd17ff7569439458517bccd79ff..e4171d400aed64d43ec115c24fa638af2afcb959 100644
--- a/lms/static/sass/_build-base-v1.scss
+++ b/lms/static/sass/_build-base-v1.scss
@@ -7,3 +7,7 @@
 @import 'base/variables';
 @import 'base/mixins';
 @import 'base/theme';
+
+// Pattern Library shims
+@import 'edx-pattern-library-shims/base/variables';
+@import 'edx-pattern-library-shims/buttons';
diff --git a/lms/static/sass/_build-lms-v2.scss b/lms/static/sass/_build-lms-v2.scss
index ef5dc816ad0642cd7daefb8d96495747122a08f2..be8a9bb4e76aabf9b90406f5e1ecd0768602ac47 100644
--- a/lms/static/sass/_build-lms-v2.scss
+++ b/lms/static/sass/_build-lms-v2.scss
@@ -21,6 +21,7 @@
 // Elements
 @import 'notifications';
 @import 'elements/controls';
+@import 'elements-v2/buttons';
 @import 'elements-v2/pagination';
 
 // Features
diff --git a/lms/static/sass/course/_info.scss b/lms/static/sass/course/_info.scss
index 60bbeb52bf8ef10ace7357381efc9fbd24cacc12..d127a9584e0ecb743886f40694205d317ce15005 100644
--- a/lms/static/sass/course/_info.scss
+++ b/lms/static/sass/course/_info.scss
@@ -1,13 +1,3 @@
-// Upgrade button
-$btn-upgrade-border-color:                $uxpl-green-base !default;
-$btn-upgrade-background:                  $uxpl-green-base !default;
-$btn-upgrade-color:                       #fcfcfc !default;
-$btn-upgrade-focus-color:                 $btn-upgrade-color !default;
-$btn-upgrade-focus-border-color:          rgb(0, 155, 0) !default;
-$btn-upgrade-focus-background:            rgb(0, 155, 0) !default;
-$btn-upgrade-active-border-color:         $uxpl-green-base !default;
-$btn-upgrade-active-background:           $uxpl-green-base !default;
-
 //// Notifications
 // Upgrade
 
@@ -142,31 +132,6 @@ div.info-wrapper {
           @include margin(0, 0, 0, auto);
           padding: $baseline/2 $baseline;
         }
-
-        .btn-upgrade {
-          @extend %btn-shims;
-
-          border-color: $btn-upgrade-border-color;
-          background: $btn-upgrade-background;
-          color: $btn-upgrade-color;
-          // STATE: hover and focus
-          &:hover,
-          &.is-hovered,
-          &:focus,
-          &.is-focused {
-            border-color: $btn-upgrade-focus-border-color;
-            background-color: $btn-upgrade-focus-background;
-            color: $btn-upgrade-focus-color;
-          }
-
-          // STATE: is disabled
-          &:disabled,
-          &.is-disabled {
-            border-color: $btn-disabled-border-color;
-            background: $btn-brand-disabled-background;
-            color: $btn-upgrade-color;
-          }
-        }
       }
     }
 
diff --git a/lms/static/sass/elements-v2/_buttons.scss b/lms/static/sass/elements-v2/_buttons.scss
new file mode 100644
index 0000000000000000000000000000000000000000..00541fc25d0d144c76a9a5f4fd8cd6a494ed5e37
--- /dev/null
+++ b/lms/static/sass/elements-v2/_buttons.scss
@@ -0,0 +1,42 @@
+// ----------------------------
+// #UPGRADE
+// ----------------------------
+$upgrade-color: #009b00 !default;
+$upgrade-dark-color: #008100 !default;
+
+.btn-upgrade {
+  @extend %btn;
+
+  border-color: $upgrade-color;
+  background: $upgrade-color;
+  color: palette(primary, x-back);
+  text-decoration: none;
+
+  // STATE: hover and focus
+  &:hover,
+  &.is-hovered,
+  &:focus,
+  &.is-focused {
+    border-color: $upgrade-dark-color;
+    background: $upgrade-dark-color;
+    text-decoration: none;
+  }
+
+  // STATE: is pressed or active
+  &:active,
+  &.is-pressed,
+  &.is-active {
+    border-color: $upgrade-dark-color;
+    background: $upgrade-dark-color;
+    text-decoration: none;
+  }
+
+  // STATE: is disabled
+  &:disabled,
+  &.is-disabled {
+    border-color: $btn-disabled-border-color;
+    background: $btn-disabled-background-color;
+    color: $btn-disabled-text-color;
+    text-decoration: none;
+  }
+}
diff --git a/lms/static/sass/features/_course-experience.scss b/lms/static/sass/features/_course-experience.scss
index fff4702801d65c1ee49995ce0b3c4caaf6126e61..5a9fe4120eb8a151e1f288e6c34d98fd4fb2480b 100644
--- a/lms/static/sass/features/_course-experience.scss
+++ b/lms/static/sass/features/_course-experience.scss
@@ -16,6 +16,7 @@
 
   .message-content {
     @include margin(0, 0, $baseline, $baseline);
+
     position: relative;
     border: 1px solid $lms-border-color;
     padding: $baseline;
@@ -60,15 +61,17 @@
     .message-header {
       font-weight: $font-semibold;
       margin-bottom: $baseline/2;
-      width: calc(100% - 40px)
+      width: calc(100% - 40px);
     }
 
-    a {
+    a:not(.btn) {
       font-weight: $font-semibold;
       text-decoration: underline;
     }
+
     .dismiss {
       @include right($baseline/4);
+
       top: $baseline/4;
       position: absolute;
       cursor: pointer;
@@ -90,6 +93,7 @@
 
         &.dismissible {
           @include right($baseline/4);
+
           position: absolute;
           top: $baseline/2;
           font-size: font-size(small);
@@ -103,6 +107,12 @@
       }
     }
   }
+
+  .message-actions {
+    display: flex;
+    margin-top: $baseline/2;
+    justify-content: flex-end;
+  }
 }
 
 // Welcome message / Latest Update message
diff --git a/lms/static/sass/features/_course-sock.scss b/lms/static/sass/features/_course-sock.scss
index 9b59dc694c429fc8faa407802e8c6328c2e33434..d08327c792a8e80404612496963503ec0b6fb95b 100644
--- a/lms/static/sass/features/_course-sock.scss
+++ b/lms/static/sass/features/_course-sock.scss
@@ -111,10 +111,6 @@
       .action-upgrade-certificate {
         position: absolute;
         right: $baseline;
-        background-color: $success-color;
-        border-color: $success-color;
-        background-image: none;
-        box-shadow: none;
 
         @media (max-width: 960px) {
           & {
@@ -142,11 +138,6 @@
             top: auto;
           }
         }
-
-        &:hover {
-          background-color: $success-color-hover;
-          border-color: $success-color-hover;
-        }
       }
     }
   }
diff --git a/lms/static/sass/features/_course-upgrade-message.scss b/lms/static/sass/features/_course-upgrade-message.scss
index e967c534c0ccbc8c9fab6f16c8c5c8bd91a23631..0a15a8d5631fdab93185ee74504e02b0f409218e 100644
--- a/lms/static/sass/features/_course-upgrade-message.scss
+++ b/lms/static/sass/features/_course-upgrade-message.scss
@@ -70,13 +70,6 @@ $upgrade-message-background-color: $blue-d1;
     color: $white;
   }
 
-  // Upgrade Button
-  .btn-upgrade {
-    @extend %btn-primary-green;
-
-    background: $uxpl-green-base;
-  }
-
   // Cert image
   .vc-hero {
     @include float(right);
diff --git a/openedx/features/course_experience/__init__.py b/openedx/features/course_experience/__init__.py
index 8c9980f5788f219e24bc56abc56c9b4d07c1af83..a7c48a506c0d65b92ef3a0912e26cf0197c13177 100644
--- a/openedx/features/course_experience/__init__.py
+++ b/openedx/features/course_experience/__init__.py
@@ -28,8 +28,12 @@ SHOW_REVIEWS_TOOL_FLAG = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'show_reviews_t
 # Waffle flag to enable the setting of course goals.
 ENABLE_COURSE_GOALS = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'enable_course_goals')
 
+# Waffle flag to control the display of the hero
 SHOW_UPGRADE_MSG_ON_COURSE_HOME = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'show_upgrade_msg_on_course_home')
 
+# Waffle flag to control the display of the upgrade deadline message
+UPGRADE_DEADLINE_MESSAGE = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'upgrade_deadline_message')
+
 # Waffle flag to switch between the 'welcome message' and 'latest update' on the course home page.
 # Important Admin Note: This is meant to be configured using waffle_utils course
 #   override only.  Either do not create the actual waffle flag, or be sure to unset the
diff --git a/openedx/features/course_experience/templates/course_experience/course-home-fragment.html b/openedx/features/course_experience/templates/course_experience/course-home-fragment.html
index 7216dae9d002b7661d453e3ffa3da7435523aa5f..7f5cfd550fd52a67ac41498db3891e0402e2f272 100644
--- a/openedx/features/course_experience/templates/course_experience/course-home-fragment.html
+++ b/openedx/features/course_experience/templates/course_experience/course-home-fragment.html
@@ -82,7 +82,7 @@ from openedx.features.course_experience import UNIFIED_COURSE_TAB_FLAG, SHOW_REV
                             </ul>
 
                             <div class="vc-cta vc-fade vc-polite-only">
-                                <a class="btn-upgrade" href="${ upgrade_url }">${_("Upgrade ({price})").format(price='$' + str(upgrade_price))}</a>
+                                <a class="btn-upgrade" href="${ upgrade_url }">${_("Upgrade ({price})").format(price=upgrade_price)}</a>
                             </div>
                         </div>
                     </div>
diff --git a/openedx/features/course_experience/templates/course_experience/course-sock-fragment.html b/openedx/features/course_experience/templates/course_experience/course-sock-fragment.html
index b569f0bf08b7d30830ecc6980ac7a201bbd3e2d0..7deac01524120060e32ae59e829befb6cca3af29 100644
--- a/openedx/features/course_experience/templates/course_experience/course-sock-fragment.html
+++ b/openedx/features/course_experience/templates/course_experience/course-sock-fragment.html
@@ -55,9 +55,9 @@ from openedx.features.course_experience import DISPLAY_COURSE_SOCK_FLAG
                     </div>
                 </div>
                 <img class="mini-cert" src="${static.url('course_experience/images/verified-cert.png')}"/>
-                <a href="/verify_student/upgrade/${course_id}/">
-                    <button type="button" class="btn btn-brand stuck-top focusable action-upgrade-certificate">
-                        Upgrade Now (${HTML(course_price)})
+                <a href="${upgrade_url}">
+                    <button type="button" class="btn btn-upgrade stuck-top focusable action-upgrade-certificate">
+                        Upgrade (${HTML(course_price)})
                     </button>
                 </a>
             </div>
diff --git a/openedx/features/course_experience/tests/views/test_course_home.py b/openedx/features/course_experience/tests/views/test_course_home.py
index a6a118d10e3f8ccee099f7abff5247717b52e572..7a2ecfcca7f9733c503b4c6fa2223bcdffd6d4b3 100644
--- a/openedx/features/course_experience/tests/views/test_course_home.py
+++ b/openedx/features/course_experience/tests/views/test_course_home.py
@@ -173,7 +173,7 @@ class TestCourseHomePage(CourseHomePageTestCase):
         course_home_url(self.course)
 
         # Fetch the view and verify the query counts
-        with self.assertNumQueries(44, table_blacklist=QUERY_COUNT_TABLE_BLACKLIST):
+        with self.assertNumQueries(45, table_blacklist=QUERY_COUNT_TABLE_BLACKLIST):
             with check_mongo_calls(4):
                 url = course_home_url(self.course)
                 self.client.get(url)
@@ -477,11 +477,9 @@ class CourseHomeFragmentViewTests(ModuleStoreTestCase):
         response = self.client.get(self.url)
         self.assertIn('vc-message', response.content)
         url = EcommerceService().get_checkout_page_url(self.verified_mode.sku)
-        expected = '<a class="btn-upgrade" href="{url}">Upgrade (${price})</a>'.format(
-            url=url,
-            price=self.verified_mode.min_price
-        )
-        self.assertIn(expected, response.content)
+        self.assertIn('<a class="btn-upgrade"', response.content)
+        self.assertIn(url, response.content)
+        self.assertIn('Upgrade (${price})</a>'.format(price=self.verified_mode.min_price), response.content)
 
     def test_no_upgrade_message_if_logged_out(self):
         self.client.logout()
diff --git a/openedx/features/course_experience/views/course_home.py b/openedx/features/course_experience/views/course_home.py
index 026eeb91a75cce5fab507c3a28b7a7e25b6f21f6..7c70b8ca99ad1677d531c4b1d97897aa780caf56 100644
--- a/openedx/features/course_experience/views/course_home.py
+++ b/openedx/features/course_experience/views/course_home.py
@@ -10,6 +10,7 @@ from django.views.decorators.cache import cache_control
 from django.views.decorators.csrf import ensure_csrf_cookie
 
 from commerce.utils import EcommerceService
+from course_modes.models import get_cosmetic_verified_display_price
 from courseware.access import has_access
 from courseware.courses import (
     can_self_enroll_in_course,
@@ -165,15 +166,8 @@ class CourseHomeFragmentView(EdxFragmentView):
 
         # TODO Add switch to control deployment
         if SHOW_UPGRADE_MSG_ON_COURSE_HOME.is_enabled(course_key) and enrollment and enrollment.upgrade_deadline:
-            verified_mode = enrollment.verified_mode
-            if verified_mode:
-                upgrade_price = verified_mode.min_price
-
-                ecommerce_service = EcommerceService()
-                if ecommerce_service.is_enabled(request.user):
-                    upgrade_url = ecommerce_service.get_checkout_page_url(verified_mode.sku)
-                else:
-                    upgrade_url = reverse('verify_student_upgrade_and_verify', args=(course_key,))
+            upgrade_url = EcommerceService().upgrade_url(request.user, course_key)
+            upgrade_price = get_cosmetic_verified_display_price(course)
 
         # Render the course home fragment
         context = {
diff --git a/openedx/features/course_experience/views/course_home_messages.py b/openedx/features/course_experience/views/course_home_messages.py
index a851695f21029f4a8192ff32a33617217bb34702..3f35c8e68f83ccb1934a69eae2fc166890783e5c 100644
--- a/openedx/features/course_experience/views/course_home_messages.py
+++ b/openedx/features/course_experience/views/course_home_messages.py
@@ -17,7 +17,7 @@ from rest_framework.reverse import reverse
 from web_fragments.fragment import Fragment
 
 from course_modes.models import CourseMode
-from courseware.courses import get_course_with_access
+from courseware.courses import get_course_date_blocks, get_course_with_access
 from lms.djangoapps.course_goals.api import get_course_goal
 from lms.djangoapps.course_goals.models import GOAL_KEY_CHOICES
 from openedx.core.djangoapps.plugin_api.views import EdxFragmentView
@@ -64,7 +64,15 @@ class CourseHomeMessageFragmentView(EdxFragmentView):
         }
 
         # Register the course home messages to be loaded on the page
-        _register_course_home_messages(request, course_id, user_access, course_start_data)
+        _register_course_home_messages(request, course, user_access, course_start_data)
+
+        # Register course date alerts
+        for course_date_block in get_course_date_blocks(course, request.user):
+            course_date_block.register_alerts(request, course)
+
+        # Register a course goal message, if appropriate
+        if _should_show_course_goal_message(request, course, user_access):
+            _register_course_goal_message(request, course)
 
         # Grab the relevant messages
         course_home_messages = list(CourseHomeMessages.user_messages(request))
@@ -73,7 +81,7 @@ class CourseHomeMessageFragmentView(EdxFragmentView):
         goal_api_url = reverse('course_goals_api:v0:course_goal-list', request=request)
 
         # Grab the logo
-        image_src = "course_experience/images/home_message_author.png"
+        image_src = 'course_experience/images/home_message_author.png'
 
         context = {
             'course_home_messages': course_home_messages,
@@ -87,24 +95,22 @@ class CourseHomeMessageFragmentView(EdxFragmentView):
         return Fragment(html)
 
 
-def _register_course_home_messages(request, course_id, user_access, course_start_data):
+def _register_course_home_messages(request, course, user_access, course_start_data):
     """
     Register messages to be shown in the course home content page.
     """
-    course_key = CourseKey.from_string(course_id)
-    course = get_course_with_access(request.user, 'load', course_key)
     if user_access['is_anonymous']:
         CourseHomeMessages.register_info_message(
             request,
             Text(_(
-                " {sign_in_link} or {register_link} and then enroll in this course."
+                '{sign_in_link} or {register_link} and then enroll in this course.'
             )).format(
-                sign_in_link=HTML("<a href='/login?next={current_url}'>{sign_in_label}</a>").format(
-                    sign_in_label=_("Sign in"),
+                sign_in_link=HTML('<a href="/login?next={current_url}">{sign_in_label}</a>').format(
+                    sign_in_label=_('Sign in'),
                     current_url=urlquote_plus(request.path),
                 ),
-                register_link=HTML("<a href='/register?next={current_url}'>{register_label}</a>").format(
-                    register_label=_("register"),
+                register_link=HTML('<a href="/register?next={current_url}">{register_label}</a>').format(
+                    register_label=_('register'),
                     current_url=urlquote_plus(request.path),
                 )
             ),
@@ -114,7 +120,7 @@ def _register_course_home_messages(request, course_id, user_access, course_start
         CourseHomeMessages.register_info_message(
             request,
             Text(_(
-                "{open_enroll_link} Enroll now{close_enroll_link} to access the full course."
+                '{open_enroll_link}Enroll now{close_enroll_link} to access the full course.'
             )).format(
                 open_enroll_link='',
                 close_enroll_link=''
@@ -123,81 +129,97 @@ def _register_course_home_messages(request, course_id, user_access, course_start
                 course_display_name=course.display_name
             )
         )
-    if user_access['is_enrolled'] and not course_start_data['already_started']:
-        CourseHomeMessages.register_info_message(
-            request,
-            Text(_(
-                "Don't forget to add a calendar reminder!"
-            )),
-            title=Text(_("Course starts in {days_until_start_string} on {course_start_date}.")).format(
-                days_until_start_string=course_start_data['days_until_start_string'],
-                course_start_date=course_start_data['course_start_date']
-            )
-        )
 
-    # Only show the set course goal message for enrolled, unverified
-    # users that have not yet set a goal in a course that allows for
-    # verified statuses.
-    has_verified_mode = CourseMode.has_verified_mode(CourseMode.modes_for_course_dict(unicode(course.id)))
-    is_already_verified = CourseEnrollment.is_enrolled_as_verified(request.user, course_key)
-    user_goal = get_course_goal(auth.get_user(request), course_key) if not request.user.is_anonymous() else None
-    if user_access['is_enrolled'] and has_verified_mode and not is_already_verified and not user_goal \
-            and ENABLE_COURSE_GOALS.is_enabled(course_key) and settings.FEATURES.get('ENABLE_COURSE_GOALS'):
-        goal_choices_html = Text(_(
-            'To start, set a course goal by selecting the option below that best describes '
-            'your learning plan. {goal_options_container}'
-        )).format(
-            goal_options_container=HTML('<div class="row goal-options-container">')
-        )
 
-        # Add the dismissible option for users that are unsure of their goal
-        goal_choices_html += Text(
-            '{initial_tag}{choice}{closing_tag}'
+def _should_show_course_goal_message(request, course, user_access):
+    """
+    Returns true if the current learner should be shown a course goal message.
+    """
+    course_key = course.id
+
+    # Don't show a message if course goals has not been enabled
+    if not ENABLE_COURSE_GOALS.is_enabled(course_key) or not settings.FEATURES.get('ENABLE_COURSE_GOALS'):
+        return False
+
+    # Don't show a message if the user is not enrolled
+    if not user_access['is_enrolled']:
+        return False
+
+    # Don't show a message if the learner has already specified a goal
+    if get_course_goal(auth.get_user(request), course_key):
+        return False
+
+    # Don't show a message if the course does not have a verified mode
+    if not CourseMode.has_verified_mode(CourseMode.modes_for_course_dict(unicode(course_key))):
+        return False
+
+    # Don't show a message if the learner has already verified
+    if CourseEnrollment.is_enrolled_as_verified(request.user, course_key):
+        return False
+
+    return True
+
+
+def _register_course_goal_message(request, course):
+    """
+    Register a message to let a learner specify a course goal.
+    """
+    goal_choices_html = Text(_(
+        'To start, set a course goal by selecting the option below that best describes '
+        'your learning plan. {goal_options_container}'
+    )).format(
+        goal_options_container=HTML('<div class="row goal-options-container">')
+    )
+
+    # Add the dismissible option for users that are unsure of their goal
+    goal_choices_html += Text(
+        '{initial_tag}{choice}{closing_tag}'
+    ).format(
+        initial_tag=HTML(
+            '<div tabindex="0" aria-label="{aria_label_choice}" class="goal-option dismissible" '
+            'data-choice="{goal_key}">'
+        ).format(
+            goal_key=GOAL_KEY_CHOICES.unsure,
+            aria_label_choice=Text(_("Set goal to: {choice}")).format(
+                choice=GOAL_KEY_CHOICES[GOAL_KEY_CHOICES.unsure]
+            ),
+        ),
+        choice=Text(_('{choice}')).format(
+            choice=GOAL_KEY_CHOICES[GOAL_KEY_CHOICES.unsure],
+        ),
+        closing_tag=HTML('</div>'),
+    )
+
+    # Add the option to set a goal to earn a certificate,
+    # complete the course or explore the course
+    goal_options = [
+        GOAL_KEY_CHOICES.certify,
+        GOAL_KEY_CHOICES.complete,
+        GOAL_KEY_CHOICES.explore
+    ]
+    for goal_key in goal_options:
+        goal_text = GOAL_KEY_CHOICES[goal_key]
+        goal_choices_html += HTML(
+            '{initial_tag}{goal_text}{closing_tag}'
         ).format(
             initial_tag=HTML(
-                '<div tabindex="0" aria-label="{aria_label_choice}" class="goal-option dismissible" '
+                '<div tabindex="0" aria-label="{aria_label_choice}" class="goal-option {col_sel} btn" '
                 'data-choice="{goal_key}">'
             ).format(
-                goal_key=GOAL_KEY_CHOICES.unsure,
-                aria_label_choice=Text(_("Set goal to: {choice}")).format(
-                    choice=GOAL_KEY_CHOICES[GOAL_KEY_CHOICES.unsure]
+                goal_key=goal_key,
+                aria_label_choice=Text(_("Set goal to: {goal_text}")).format(
+                    goal_text=Text(_(goal_text))
                 ),
+                col_sel='col-' + str(int(math.floor(12 / len(goal_options))))
             ),
-            choice=Text(_('{choice}')).format(
-                choice=GOAL_KEY_CHOICES[GOAL_KEY_CHOICES.unsure],
-            ),
-            closing_tag=HTML('</div>'),
+            goal_text=goal_text,
+            closing_tag=HTML('</div>')
         )
 
-        # Add the option to set a goal to earn a certificate,
-        # complete the course or explore the course
-        goal_options = [GOAL_KEY_CHOICES.certify, GOAL_KEY_CHOICES.complete, GOAL_KEY_CHOICES.explore]
-        for goal_key in goal_options:
-            goal_text = GOAL_KEY_CHOICES[goal_key]
-            goal_choices_html += HTML(
-                '{initial_tag}{goal_text}{closing_tag}'
-            ).format(
-                initial_tag=HTML(
-                    '<div tabindex="0" aria-label="{aria_label_choice}" class="goal-option {col_sel} btn" '
-                    'data-choice="{goal_key}">'
-                ).format(
-                    goal_key=goal_key,
-                    aria_label_choice=Text(_("Set goal to: {goal_text}")).format(
-                        goal_text=Text(_(goal_text))
-                    ),
-                    col_sel='col-' + str(int(math.floor(12 / len(goal_options))))
-                ),
-                goal_text=goal_text,
-                closing_tag=HTML('</div>')
-            )
-
-        CourseHomeMessages.register_info_message(
-            request,
-            HTML('{goal_choices_html}{closing_tag}').format(
-                goal_choices_html=goal_choices_html,
-                closing_tag=HTML('</div>')
-            ),
-            title=Text(_('Welcome to {course_display_name}')).format(
-                course_display_name=course.display_name
-            )
+    CourseHomeMessages.register_info_message(
+        request,
+        goal_choices_html,
+        title=Text(_('Welcome to {course_display_name}')).format(
+            course_display_name=course.display_name
         )
+    )
diff --git a/openedx/features/course_experience/views/course_sock.py b/openedx/features/course_experience/views/course_sock.py
index 20f88e06ca6c2224d8570b1c5a81a9ed1b107fd3..9c27c77183c32f0e9d219bda6bfadd4f3549d1a7 100644
--- a/openedx/features/course_experience/views/course_sock.py
+++ b/openedx/features/course_experience/views/course_sock.py
@@ -6,10 +6,11 @@ from django.utils.translation import get_language
 from opaque_keys.edx.keys import CourseKey
 from web_fragments.fragment import Fragment
 
-from student.models import CourseEnrollment
+from commerce.utils import EcommerceService
 from course_modes.models import CourseMode, get_cosmetic_verified_display_price
 from courseware.date_summary import VerifiedUpgradeDeadlineDate
 from openedx.core.djangoapps.plugin_api.views import EdxFragmentView
+from student.models import CourseEnrollment
 
 
 class CourseSockFragmentView(EdxFragmentView):
@@ -44,13 +45,15 @@ class CourseSockFragmentView(EdxFragmentView):
             not deadline_has_passed and get_language() == 'en'
         )
 
-        # Get the price of the course and format correctly
+        # Get information about the upgrade
         course_price = get_cosmetic_verified_display_price(course)
+        upgrade_url = EcommerceService().upgrade_url(request.user, course_key)
 
         context = {
             'show_course_sock': show_course_sock,
             'course_price': course_price,
-            'course_id': course.id
+            'course_id': course.id,
+            'upgrade_url': upgrade_url,
         }
 
         return context