Merge pull request #6924 from edx/will/ECOM-1045
ECOM-1045: Add messaging for when students miss the verification deadline
This commit is contained in:
@@ -1576,30 +1576,6 @@ class CertificateItem(OrderItem):
|
||||
self.course_enrollment.change_mode(self.mode)
|
||||
self.course_enrollment.activate()
|
||||
|
||||
@property
|
||||
def single_item_receipt_template(self):
|
||||
if self.mode in ('verified', 'professional'):
|
||||
return 'shoppingcart/verified_cert_receipt.html'
|
||||
else:
|
||||
return super(CertificateItem, self).single_item_receipt_template
|
||||
|
||||
@property
|
||||
def single_item_receipt_context(self):
|
||||
course = modulestore().get_course(self.course_id)
|
||||
return {
|
||||
"course_id": self.course_id,
|
||||
"course_name": course.display_name_with_default,
|
||||
"course_org": course.display_org_with_default,
|
||||
"course_num": course.display_number_with_default,
|
||||
"course_start_date_text": course.start_datetime_text(),
|
||||
"course_has_started": course.start > datetime.today().replace(tzinfo=pytz.utc),
|
||||
"course_root_url": reverse(
|
||||
'course_root',
|
||||
kwargs={'course_id': self.course_id.to_deprecated_string()} # pylint: disable=no-member
|
||||
),
|
||||
"dashboard_url": reverse('dashboard'),
|
||||
}
|
||||
|
||||
def additional_instruction_text(self):
|
||||
refund_reminder = _(
|
||||
"You have up to two weeks into the course to unenroll from the Verified Certificate option "
|
||||
|
||||
@@ -610,13 +610,10 @@ class CertificateItemTest(ModuleStoreTestCase):
|
||||
def test_single_item_template(self):
|
||||
cart = Order.get_cart_for_user(user=self.user)
|
||||
cert_item = CertificateItem.add_to_order(cart, self.course_key, self.cost, 'verified')
|
||||
|
||||
self.assertEquals(cert_item.single_item_receipt_template,
|
||||
'shoppingcart/verified_cert_receipt.html')
|
||||
self.assertEquals(cert_item.single_item_receipt_template, 'shoppingcart/receipt.html')
|
||||
|
||||
cert_item = CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor')
|
||||
self.assertEquals(cert_item.single_item_receipt_template,
|
||||
'shoppingcart/receipt.html')
|
||||
self.assertEquals(cert_item.single_item_receipt_template, 'shoppingcart/receipt.html')
|
||||
|
||||
@override_settings(
|
||||
SEGMENT_IO_LMS_KEY="foobar",
|
||||
|
||||
@@ -1300,7 +1300,7 @@ class ReceiptRedirectTest(ModuleStoreTestCase):
|
||||
password=self.PASSWORD
|
||||
)
|
||||
|
||||
def test_show_receipt_redirect_to_verify_student(self):
|
||||
def test_postpay_callback_redirect_to_verify_student(self):
|
||||
# Create other carts first
|
||||
# This ensures that the order ID and order item IDs do not match
|
||||
Order.get_cart_for_user(self.user).start_purchase()
|
||||
@@ -1315,11 +1315,13 @@ class ReceiptRedirectTest(ModuleStoreTestCase):
|
||||
self.COST,
|
||||
'verified'
|
||||
)
|
||||
self.cart.purchase()
|
||||
self.cart.start_purchase()
|
||||
|
||||
# Visit the receipt page
|
||||
url = reverse('shoppingcart.views.show_receipt', args=[self.cart.id])
|
||||
resp = self.client.get(url)
|
||||
# Simulate hitting the post-pay callback
|
||||
with patch('shoppingcart.views.process_postpay_callback') as mock_process:
|
||||
mock_process.return_value = {'success': True, 'order': self.cart}
|
||||
url = reverse('shoppingcart.views.postpay_callback')
|
||||
resp = self.client.post(url, follow=True)
|
||||
|
||||
# Expect to be redirected to the payment confirmation
|
||||
# page in the verify_student app
|
||||
@@ -1330,8 +1332,7 @@ class ReceiptRedirectTest(ModuleStoreTestCase):
|
||||
redirect_url += '?payment-order-num={order_num}'.format(
|
||||
order_num=self.cart.id
|
||||
)
|
||||
|
||||
self.assertRedirects(resp, redirect_url)
|
||||
self.assertIn(redirect_url, resp.redirect_chain[0][0])
|
||||
|
||||
|
||||
@patch.dict('django.conf.settings.FEATURES', {'ENABLE_PAID_COURSE_REGISTRATION': True})
|
||||
|
||||
@@ -37,8 +37,9 @@ from .exceptions import (
|
||||
from .models import (
|
||||
Order, OrderTypes,
|
||||
PaidCourseRegistration, OrderItem, Coupon,
|
||||
CouponRedemption, CourseRegistrationCode, RegistrationCodeRedemption,
|
||||
CourseRegCodeItem, Donation, DonationConfiguration
|
||||
CertificateItem, CouponRedemption, CourseRegistrationCode,
|
||||
RegistrationCodeRedemption, CourseRegCodeItem,
|
||||
Donation, DonationConfiguration
|
||||
)
|
||||
from .processors import (
|
||||
process_postpay_callback, render_purchase_form_html,
|
||||
@@ -612,6 +613,43 @@ def donate(request):
|
||||
return HttpResponse(response_params, content_type="text/json")
|
||||
|
||||
|
||||
def _get_verify_flow_redirect(order):
|
||||
"""Check if we're in the verification flow and redirect if necessary.
|
||||
|
||||
Arguments:
|
||||
order (Order): The order received by the post-pay callback.
|
||||
|
||||
Returns:
|
||||
HttpResponseRedirect or None
|
||||
|
||||
"""
|
||||
# See if the order contained any certificate items
|
||||
# If so, the user is coming from the payment/verification flow.
|
||||
cert_items = CertificateItem.objects.filter(order=order)
|
||||
|
||||
if cert_items.count() > 0:
|
||||
# Currently, we allow the purchase of only one verified
|
||||
# enrollment at a time; if there are more than one,
|
||||
# this will choose the first.
|
||||
if cert_items.count() > 1:
|
||||
log.warning(
|
||||
u"More than one certificate item in order %s; "
|
||||
u"continuing with the payment/verification flow for "
|
||||
u"the first order item (course %s).",
|
||||
order.id, cert_items[0].course_id
|
||||
)
|
||||
|
||||
course_id = cert_items[0].course_id
|
||||
url = reverse(
|
||||
'verify_student_payment_confirmation',
|
||||
kwargs={'course_id': unicode(course_id)}
|
||||
)
|
||||
# Add a query string param for the order ID
|
||||
# This allows the view to query for the receipt information later.
|
||||
url += '?payment-order-num={order_num}'.format(order_num=order.id)
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
@require_POST
|
||||
def postpay_callback(request):
|
||||
@@ -626,7 +664,16 @@ def postpay_callback(request):
|
||||
"""
|
||||
params = request.POST.dict()
|
||||
result = process_postpay_callback(params)
|
||||
|
||||
if result['success']:
|
||||
# See if this payment occurred as part of the verification flow process
|
||||
# If so, send the user back into the flow so they have the option
|
||||
# to continue with verification.
|
||||
verify_flow_redirect = _get_verify_flow_redirect(result['order'])
|
||||
if verify_flow_redirect is not None:
|
||||
return verify_flow_redirect
|
||||
|
||||
# Otherwise, send the user to the receipt page
|
||||
return HttpResponseRedirect(reverse('shoppingcart.views.show_receipt', args=[result['order'].id]))
|
||||
else:
|
||||
return render_to_response('shoppingcart/error.html', {'order': result['order'],
|
||||
@@ -869,29 +916,13 @@ def _show_receipt_html(request, order):
|
||||
'reg_code_info_list': reg_code_info_list,
|
||||
'order_purchase_date': order.purchase_time.strftime("%B %d, %Y"),
|
||||
}
|
||||
|
||||
# We want to have the ability to override the default receipt page when
|
||||
# there is only one item in the order.
|
||||
if order_items.count() == 1:
|
||||
receipt_template = order_items[0].single_item_receipt_template
|
||||
context.update(order_items[0].single_item_receipt_context)
|
||||
|
||||
# Ideally, the shoppingcart app would own the receipt view. However,
|
||||
# as a result of changes made to the payment and verification flows as
|
||||
# part of an A/B test, the verify_student app owns it instead. This is
|
||||
# left over, and will be made more general in the future.
|
||||
if receipt_template == 'shoppingcart/verified_cert_receipt.html':
|
||||
url = reverse(
|
||||
'verify_student_payment_confirmation',
|
||||
kwargs={'course_id': unicode(order_items[0].course_id)}
|
||||
)
|
||||
|
||||
# Add a query string param for the order ID
|
||||
# This allows the view to query for the receipt information later.
|
||||
url += '?payment-order-num={order_num}'.format(
|
||||
order_num=order_items[0].order.id
|
||||
)
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
return render_to_response(receipt_template, context)
|
||||
|
||||
|
||||
|
||||
@@ -603,6 +603,25 @@ class TestPayAndVerifyView(ModuleStoreTestCase):
|
||||
data = self._get_page_data(response)
|
||||
self.assertEqual(data['verification_deadline'], "Jan 02, 2999 at 00:00 UTC")
|
||||
|
||||
def test_course_mode_expired(self):
|
||||
course = self._create_course("verified")
|
||||
mode = CourseMode.objects.get(
|
||||
course_id=course.id,
|
||||
mode_slug="verified"
|
||||
)
|
||||
expiration = datetime(1999, 1, 2, tzinfo=pytz.UTC)
|
||||
mode.expiration_datetime = expiration
|
||||
mode.save()
|
||||
|
||||
# Need to be enrolled
|
||||
self._enroll(course.id, "verified")
|
||||
|
||||
# The course mode has expired, so expect an explanation
|
||||
# to the student that the deadline has passed
|
||||
response = self._get_page("verify_student_verify_later", course.id)
|
||||
self.assertContains(response, "verification deadline")
|
||||
self.assertContains(response, "Jan 02, 1999 at 00:00 UTC")
|
||||
|
||||
def _create_course(self, *course_modes, **kwargs):
|
||||
"""Create a new course with the specified course modes. """
|
||||
course = CourseFactory.create()
|
||||
|
||||
@@ -256,20 +256,36 @@ class PayAndVerifyView(View):
|
||||
log.warn(u"No course specified for verification flow request.")
|
||||
raise Http404
|
||||
|
||||
# Verify that the course has a verified mode
|
||||
course_mode = CourseMode.verified_mode_for_course(course_key)
|
||||
if course_mode is None:
|
||||
# Check that the course has an unexpired verified mode
|
||||
course_mode, expired_course_mode = self._get_verified_modes_for_course(course_key)
|
||||
|
||||
if course_mode is not None:
|
||||
log.info(
|
||||
u"Entering verified workflow for user '%s', course '%s', with current step '%s'.",
|
||||
request.user.id, course_id, current_step
|
||||
)
|
||||
elif expired_course_mode is not None:
|
||||
# Check if there is an *expired* verified course mode;
|
||||
# if so, we should show a message explaining that the verification
|
||||
# deadline has passed.
|
||||
log.info(u"Verification deadline for '%s' has passed.", course_id)
|
||||
context = {
|
||||
'course': course,
|
||||
'deadline': (
|
||||
get_default_time_display(expired_course_mode.expiration_datetime)
|
||||
if expired_course_mode.expiration_datetime else ""
|
||||
)
|
||||
}
|
||||
return render_to_response("verify_student/missed_verification_deadline.html", context)
|
||||
else:
|
||||
# Otherwise, there has never been a verified mode,
|
||||
# so return a page not found response.
|
||||
log.warn(
|
||||
u"No verified course mode found for course '{course_id}' for verification flow request"
|
||||
.format(course_id=course_id)
|
||||
u"No verified course mode found for course '%s' for verification flow request",
|
||||
course_id
|
||||
)
|
||||
raise Http404
|
||||
|
||||
log.info(
|
||||
u"Entering verified workflow for user '{user}', course '{course_id}', with current step '{current_step}'."
|
||||
.format(user=request.user, course_id=course_id, current_step=current_step)
|
||||
)
|
||||
|
||||
# Check whether the user has verified, paid, and enrolled.
|
||||
# A user is considered "paid" if he or she has an enrollment
|
||||
# with a paid course mode (such as "verified").
|
||||
@@ -427,6 +443,31 @@ class PayAndVerifyView(View):
|
||||
if url is not None:
|
||||
return redirect(url)
|
||||
|
||||
def _get_verified_modes_for_course(self, course_key):
|
||||
"""Retrieve unexpired and expired verified modes for a course.
|
||||
|
||||
Arguments:
|
||||
course_key (CourseKey): The location of the course.
|
||||
|
||||
Returns:
|
||||
Tuple of `(verified_mode, expired_verified_mode)`. If provided,
|
||||
`verified_mode` is an *unexpired* verified mode for the course.
|
||||
If provided, `expired_verified_mode` is an *expired* verified
|
||||
mode for the course. Either of these may be None.
|
||||
|
||||
"""
|
||||
# Retrieve all the modes at once to reduce the number of database queries
|
||||
all_modes, unexpired_modes = CourseMode.all_and_unexpired_modes_for_courses([course_key])
|
||||
|
||||
# Find an unexpired verified mode
|
||||
verified_mode = CourseMode.verified_mode_for_course(course_key, modes=unexpired_modes[course_key])
|
||||
expired_verified_mode = None
|
||||
|
||||
if verified_mode is None:
|
||||
expired_verified_mode = CourseMode.verified_mode_for_course(course_key, modes=all_modes[course_key])
|
||||
|
||||
return (verified_mode, expired_verified_mode)
|
||||
|
||||
def _display_steps(self, always_show_payment, already_verified, already_paid):
|
||||
"""Determine which steps to display to the user.
|
||||
|
||||
|
||||
@@ -1,263 +0,0 @@
|
||||
<%! from django.utils.translation import ugettext as _ %>
|
||||
|
||||
<%inherit file="../main.html" />
|
||||
<%block name="bodyclass">register verification-process step-confirmation</%block>
|
||||
|
||||
<%block name="pagetitle">${_("Receipt (Order")} ${order.id})</%block>
|
||||
|
||||
<%block name="content">
|
||||
% if notification is not UNDEFINED:
|
||||
<section class="notification">
|
||||
${notification}
|
||||
</section>
|
||||
% endif
|
||||
|
||||
<div class="container">
|
||||
<section class="wrapper">
|
||||
|
||||
<header class="page-header">
|
||||
<h2 class="title">
|
||||
|
||||
<span class="wrapper-sts">
|
||||
<span class="sts-label">${_("You are now registered for: ")}</span>
|
||||
<span class="sts-course-org">${course_org}</span>
|
||||
<span class="sts-course-number">${course_num}</span>
|
||||
<span class="sts-course-name">${course_name}</span>
|
||||
</span>
|
||||
|
||||
<span class="sts-track">
|
||||
<span class="sts-track-value">
|
||||
<span class="context">${_("Registered as: ")}</span> ${_("ID Verified")}
|
||||
</span>
|
||||
</span>
|
||||
</h2>
|
||||
</header>
|
||||
|
||||
<div class="wrapper-progress">
|
||||
<section class="progress">
|
||||
<h3 class="sr title">${_("Your Progress")}</h3>
|
||||
|
||||
<ol class="progress-steps">
|
||||
<li class="progress-step is-current" id="progress-step0">
|
||||
<span class="wrapper-step-number"><span class="step-number">0</span></span>
|
||||
<span class="step-name"><span class="sr">${_("Current Step: ")}</span>${_("Intro")}</span>
|
||||
</li>
|
||||
|
||||
<li class="progress-step" id="progress-step1">
|
||||
<span class="wrapper-step-number"><span class="step-number">1</span></span>
|
||||
<span class="step-name">${_("Take Photo")}</span>
|
||||
</li>
|
||||
|
||||
<li class="progress-step" id="progress-step2">
|
||||
<span class="wrapper-step-number"><span class="step-number">2</span></span>
|
||||
<span class="step-name">${_("Take ID Photo")}</span>
|
||||
</li>
|
||||
|
||||
<li class="progress-step" id="progress-step3">
|
||||
<span class="wrapper-step-number"><span class="step-number">3</span></span>
|
||||
<span class="step-name">${_("Review")}</span>
|
||||
</li>
|
||||
|
||||
<li class="progress-step" id="progress-step4">
|
||||
<span class="wrapper-step-number"><span class="step-number">4</span></span>
|
||||
<span class="step-name">${_("Make Payment")}</span>
|
||||
</li>
|
||||
|
||||
<li class="progress-step progress-step-icon" id="progress-step5">
|
||||
<span class="wrapper-step-number"><span class="step-number">
|
||||
<i class="icon fa fa-check-square-o"></i>
|
||||
</span></span>
|
||||
<span class="step-name"><span class="sr">${_("Current Step: ")}</span>${_("Confirmation")}</span>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<span class="progress-sts">
|
||||
<span class="progress-sts-value"></span>
|
||||
</span>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="wrapper-content-main">
|
||||
<article class="content-main">
|
||||
<h3 class="title">${_("Congratulations! You are now verified on ")} ${_(settings.PLATFORM_NAME)}.</h3>
|
||||
|
||||
<div class="instruction">
|
||||
<p>${_("You are now registered as a verified student! Your registration details are below.")}</p>
|
||||
</div>
|
||||
|
||||
<ul class="list-info">
|
||||
<li class="info-item course-info">
|
||||
<h4 class="title">${_("You are registered for:")}</h4>
|
||||
|
||||
<div class="wrapper-report">
|
||||
<table class="report report-course">
|
||||
<caption class="sr">${_("A list of courses you have just registered for as a verified student")}</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" >${_("Course")}</th>
|
||||
<th scope="col" >${_("Status")}</th>
|
||||
<th scope="col" ><span class="sr">${_("Options")}</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
% for item, course in shoppingcart_items:
|
||||
<tr>
|
||||
<td>${item.line_desc}</td>
|
||||
<td>
|
||||
${_("Starts: {start_date}").format(start_date=course_start_date_text)}
|
||||
</td>
|
||||
<td class="options">
|
||||
%if course_has_started:
|
||||
<a class="action action-course" href="${course_root_url}">${_("Go to Course")}</a>
|
||||
%else:
|
||||
%endif
|
||||
</td>
|
||||
</tr>
|
||||
% endfor
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr class="course-actions">
|
||||
<td colspan="3">
|
||||
<a class="action action-dashboard" href="${dashboard_url}">${_("Go to your Dashboard")}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="info-item verification-info">
|
||||
<h4 class="title">${_("Verified Status")}</h4>
|
||||
|
||||
<div class="copy">
|
||||
<p>${_("We have received your identification details to verify your identity. If there is a problem with any of the items, we will contact you to resubmit. You can now register for any of the verified certificate courses this semester without having to re-verify.")}</p>
|
||||
|
||||
<p>${_("The professor will ask you to periodically submit a new photo to verify your work during the course (usually at exam times).")}</p>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="info-item payment-info">
|
||||
<h4 class="title">${_("Payment Details")}</h4>
|
||||
|
||||
<div class="copy">
|
||||
<p>${_("Please print this page for your records; it serves as your receipt. You will also receive an email with the same information.")}</p>
|
||||
</div>
|
||||
|
||||
<div class="wrapper-report">
|
||||
<table class="report report-receipt">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" >${_("Order No.")}</th>
|
||||
<th scope="col" >${_("Description")}</th>
|
||||
<th scope="col" >${_("Date")}</th>
|
||||
<th scope="col" >${_("Description")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
% for item, course in shoppingcart_items:
|
||||
<tr>
|
||||
% if item.status == "purchased":
|
||||
<td>${order.id}</td>
|
||||
<td>${item.line_desc}</td>
|
||||
<td>${order.purchase_time.date().isoformat()}</td>
|
||||
<td>${"{0:0.2f}".format(item.line_cost)} (${item.currency.upper()})</td>
|
||||
|
||||
% elif item.status == "refunded":
|
||||
<td><del>${order.id}</del></td>
|
||||
<td><del>${item.line_desc}</del></td>
|
||||
<td><del>${order.purchase_time.date().isoformat()}</del></td>
|
||||
<td><del>${"{0:0.2f}".format(item.line_cost)} (${item.currency.upper()})</del></td>
|
||||
% endif
|
||||
</tr>
|
||||
% endfor
|
||||
</tbody>
|
||||
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th scope="row" class="total-label" colspan="1">${_("Total")}</th>
|
||||
<td claass="total-value" colspan="3">
|
||||
<span class="value-amount">${"{0:0.2f}".format(order.total_cost)} </span>
|
||||
<span class="value-currency">(${item.currency.upper()})</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
||||
% if any_refunds:
|
||||
<div class="msg msg-refunds">
|
||||
<h4 class="title sr">Please Note:</h4>
|
||||
<div class="copy">
|
||||
## Translators: Please keep the "<del>" and "</del>" tags around your translation of the word "this" in your translation.
|
||||
<p>${_("Note: items with strikethough like <del>this</del> have been refunded.")}</p>
|
||||
</div>
|
||||
</div>
|
||||
% endif
|
||||
</div>
|
||||
|
||||
<div class="copy">
|
||||
<p>${_("Billed To")}:
|
||||
<span class="name-first">${order.bill_to_first}</span> <span class="name-last">${order.bill_to_last}</span> (<span class="address-city">${order.bill_to_city}</span>, <span class="address-state">${order.bill_to_state}</span> <span class="address-postalcode">${order.bill_to_postalcode}</span> <span class="address-country">${order.bill_to_country.upper()}</span>)
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<%doc>
|
||||
<li class="info-item billing-info">
|
||||
<h4 class="title">${_("Billing Information")}</h4>
|
||||
|
||||
<div class="wrapper-report">
|
||||
<table class="report report-billing">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">${_("Billed To")}</th>
|
||||
<th scope="col">${_("Billing Address")}</th>
|
||||
<th scope="col">${_("Payment Method Type")}</th>
|
||||
<th scope="col">${_("Payment Method Details")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="billing-to">
|
||||
${order.bill_to_first} ${order.bill_to_last}
|
||||
</td>
|
||||
|
||||
<td class="billing-address">
|
||||
<span class="address-street1">${order.bill_to_street1}</span>
|
||||
<span class="address-street2">${order.bill_to_street2}</span>
|
||||
|
||||
<span class="address-city">${order.bill_to_street2}</span>,
|
||||
<span class="address-state">${order.bill_to_state}</span>
|
||||
<span class="address-postalcode">${order.bill_to_postalcode}</span>
|
||||
|
||||
<span class="address-country">${order.bill_to_country.upper()}</span>
|
||||
</td>
|
||||
|
||||
<td class="billing-methodtype">
|
||||
${order.bill_to_cardtype}
|
||||
</td>
|
||||
<td class="method-details">
|
||||
${order.bill_to_ccnum}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th scope="row" class="total-label" colspan="1">${_("Total")}</th>
|
||||
<td class="total-value" colspan="3"><span class="value-amount">${"{0:0.2f}".format(order.total_cost)}</span> <span class="value-currency">(${item.currency.upper()})</span></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</li>
|
||||
</%doc>
|
||||
</ul>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
</%block>
|
||||
@@ -0,0 +1,18 @@
|
||||
<%! from django.utils.translation import ugettext as _ %>
|
||||
<%namespace name='static' file='../static_content.html'/>
|
||||
|
||||
<%inherit file="../main.html" />
|
||||
|
||||
<%block name="pagetitle">${_("Verification Deadline Has Passed")}</%block>
|
||||
|
||||
<%block name="content">
|
||||
<section class="outside-app">
|
||||
<p>${_(
|
||||
u"The verification deadline for {course_name} was {date}. "
|
||||
u"Verification is no longer available."
|
||||
).format(
|
||||
course_name=course.display_name,
|
||||
date=deadline
|
||||
)}</p>
|
||||
</section>
|
||||
</%block>
|
||||
Reference in New Issue
Block a user