Commit 045e69f3 by Julia Hansbrough

Can check verified-ness and expiration date

parent b4895140
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
Add and create new modes for running courses on this particular LMS Add and create new modes for running courses on this particular LMS
""" """
import pytz import pytz
from datetime import datetime from datetime import datetime, date
from django.db import models from django.db import models
from collections import namedtuple from collections import namedtuple
...@@ -101,6 +101,17 @@ class CourseMode(models.Model): ...@@ -101,6 +101,17 @@ class CourseMode(models.Model):
modes = cls.modes_for_course(course_id) modes = cls.modes_for_course(course_id)
return min(mode.min_price for mode in modes if mode.currency == currency) return min(mode.min_price for mode in modes if mode.currency == currency)
@classmethod
def refund_expiration_date(cls, course_id, mode_slug):
"""
Returns the expiration date for verified certificate refunds. After this date, refunds are
no longer possible. Note that this is currently set to be identical to the expiration date for
verified cert signups, but this could be changed in the future
"""
print "TODO fix this"
return date(1990, 1, 1)
#return cls.mode_for_course(course_id,mode_slug).expiration_date
def __unicode__(self): def __unicode__(self):
return u"{} : {}, min={}, prices={}".format( return u"{} : {}, min={}, prices={}".format(
self.course_id, self.mode_slug, self.min_price, self.suggested_prices self.course_id, self.mode_slug, self.min_price, self.suggested_prices
......
from course_modes.models import CourseMode from course_modes.models import CourseMode
from factory import DjangoModelFactory from factory import DjangoModelFactory
import datetime
# Factories don't have __init__ methods, and are self documenting # Factories don't have __init__ methods, and are self documenting
# pylint: disable=W0232 # pylint: disable=W0232
...@@ -11,3 +12,4 @@ class CourseModeFactory(DjangoModelFactory): ...@@ -11,3 +12,4 @@ class CourseModeFactory(DjangoModelFactory):
mode_display_name = 'audit course' mode_display_name = 'audit course'
min_price = 0 min_price = 0
currency = 'usd' currency = 'usd'
expiration_date = datetime.date(1990, 1, 1)
...@@ -5,7 +5,7 @@ when you run "manage.py test". ...@@ -5,7 +5,7 @@ when you run "manage.py test".
Replace this with more appropriate tests for your application. Replace this with more appropriate tests for your application.
""" """
from datetime import datetime, timedelta from datetime import datetime, date, timedelta
import pytz import pytz
from django.test import TestCase from django.test import TestCase
...@@ -20,6 +20,7 @@ class CourseModeModelTest(TestCase): ...@@ -20,6 +20,7 @@ class CourseModeModelTest(TestCase):
def setUp(self): def setUp(self):
self.course_id = 'TestCourse' self.course_id = 'TestCourse'
CourseMode.objects.all().delete() CourseMode.objects.all().delete()
#todo use different default date
def create_mode(self, mode_slug, mode_name, min_price=0, suggested_prices='', currency='usd'): def create_mode(self, mode_slug, mode_name, min_price=0, suggested_prices='', currency='usd'):
""" """
...@@ -31,7 +32,7 @@ class CourseModeModelTest(TestCase): ...@@ -31,7 +32,7 @@ class CourseModeModelTest(TestCase):
mode_slug=mode_slug, mode_slug=mode_slug,
min_price=min_price, min_price=min_price,
suggested_prices=suggested_prices, suggested_prices=suggested_prices,
currency=currency currency=currency,
) )
def test_modes_for_course_empty(self): def test_modes_for_course_empty(self):
...@@ -112,3 +113,9 @@ class CourseModeModelTest(TestCase): ...@@ -112,3 +113,9 @@ class CourseModeModelTest(TestCase):
modes = CourseMode.modes_for_course('second_test_course') modes = CourseMode.modes_for_course('second_test_course')
self.assertEqual([CourseMode.DEFAULT_MODE], modes) self.assertEqual([CourseMode.DEFAULT_MODE], modes)
def test_refund_expiration_date(self):
self.create_mode('verified', 'Verified Certificate')
modes = CourseMode.modes_for_course(self.course_id)
mode = Mode(u'verified', u'Verified Certificate', 0, '', 'usd')
self.assertEqual(CourseMode.refund_expiration_date(self.course_id, 'verified'), date(1990, 1, 1))
...@@ -422,3 +422,28 @@ class PaidRegistrationTest(ModuleStoreTestCase): ...@@ -422,3 +422,28 @@ class PaidRegistrationTest(ModuleStoreTestCase):
self.assertEqual(response.content, reverse('shoppingcart.views.show_cart')) self.assertEqual(response.content, reverse('shoppingcart.views.show_cart'))
self.assertTrue(shoppingcart.models.PaidCourseRegistration.contained_in_order( self.assertTrue(shoppingcart.models.PaidCourseRegistration.contained_in_order(
shoppingcart.models.Order.get_cart_for_user(self.user), self.course.id)) shoppingcart.models.Order.get_cart_for_user(self.user), self.course.id))
@override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE)
class CertificateItemTest(ModuleStoreTestCase):
"""
Tests for paid certificate functionality (verified student), involves shoppingcart
"""
# test data
COURSE_SLUG = "100"
COURSE_NAME = "test_course"
COURSE_ORG = "EDX"
def setUp(self):
# Create course
self.req_factory = RequestFactory()
self.course = CourseFactory.create(org=self.COURSE_ORG, display_name=self.COURSE_NAME, number=self.COURSE_SLUG)
self.assertIsNotNone(self.course)
self.user = User.objects.create(username="test", email="test@test.org")
def test_unenroll_and_refund(self):
request = self.req_factory.post(reverse('change_enrollment'), {'course_id': self.course.id, 'enrollment_action': 'unenroll'})
request.user = self.user
response = change_enrollment(request)
self.assertEqual(response.status_code, 200)
# add more later; see if this even works
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
Student Views Student Views
""" """
import datetime import datetime
from datetime import date
import json import json
import logging import logging
import random import random
...@@ -65,6 +66,7 @@ import external_auth.views ...@@ -65,6 +66,7 @@ import external_auth.views
from bulk_email.models import Optout, CourseAuthorization from bulk_email.models import Optout, CourseAuthorization
import shoppingcart import shoppingcart
from shoppingcart.models import (Order, OrderItem, CertificateItem)
import track.views import track.views
...@@ -300,6 +302,7 @@ def dashboard(request): ...@@ -300,6 +302,7 @@ def dashboard(request):
# exist (because the course IDs have changed). Still, we don't delete those # exist (because the course IDs have changed). Still, we don't delete those
# enrollments, because it could have been a data push snafu. # enrollments, because it could have been a data push snafu.
courses = [] courses = []
refund_status = []
for enrollment in CourseEnrollment.enrollments_for_user(user): for enrollment in CourseEnrollment.enrollments_for_user(user):
try: try:
courses.append((course_from_id(enrollment.course_id), enrollment)) courses.append((course_from_id(enrollment.course_id), enrollment))
...@@ -335,15 +338,19 @@ def dashboard(request): ...@@ -335,15 +338,19 @@ def dashboard(request):
CourseAuthorization.instructor_email_enabled(course.id) CourseAuthorization.instructor_email_enabled(course.id)
) )
) )
# Verification Attempts # Verification Attempts
verification_status, verification_msg = SoftwareSecurePhotoVerification.user_status(user) verification_status, verification_msg = SoftwareSecurePhotoVerification.user_status(user)
show_refund_option_for = frozenset(course.id for course, _enrollment in courses
if (has_access(request.user, course, 'refund') and (_enrollment.mode == "verified")))
# get info w.r.t ExternalAuthMap # get info w.r.t ExternalAuthMap
external_auth_map = None external_auth_map = None
try: try:
external_auth_map = ExternalAuthMap.objects.get(user=user) external_auth_map = ExternalAuthMap.objects.get(user=user)
except ExternalAuthMap.DoesNotExist: except ExternalAuthMap.DoesNotExist:
pass pass
context = {'courses': courses, context = {'courses': courses,
'course_optouts': course_optouts, 'course_optouts': course_optouts,
'message': message, 'message': message,
...@@ -356,6 +363,7 @@ def dashboard(request): ...@@ -356,6 +363,7 @@ def dashboard(request):
'show_email_settings_for': show_email_settings_for, 'show_email_settings_for': show_email_settings_for,
'verification_status': verification_status, 'verification_status': verification_status,
'verification_msg': verification_msg, 'verification_msg': verification_msg,
'show_refund_option_for': show_refund_option_for,
} }
return render_to_response('dashboard.html', context) return render_to_response('dashboard.html', context)
...@@ -424,6 +432,8 @@ def change_enrollment(request): ...@@ -424,6 +432,8 @@ def change_enrollment(request):
.format(user.username, course_id)) .format(user.username, course_id))
return HttpResponseBadRequest(_("Course id is invalid")) return HttpResponseBadRequest(_("Course id is invalid"))
course = course_from_id(course_id)
if not has_access(user, course, 'enroll'): if not has_access(user, course, 'enroll'):
return HttpResponseBadRequest(_("Enrollment is closed")) return HttpResponseBadRequest(_("Enrollment is closed"))
...@@ -464,6 +474,32 @@ def change_enrollment(request): ...@@ -464,6 +474,32 @@ def change_enrollment(request):
elif action == "unenroll": elif action == "unenroll":
try: try:
course = course_from_id(course_id)
except ItemNotFoundError:
log.warning("User {0} tried to unenroll from non-existent course {1}"
.format(user.username, course_id))
return HttpResponseBadRequest(_("Course id is invalid"))
course = course_from_id(course_id)
verified = CourseEnrollment.enrollment_mode_for_user(user, course_id)
# did they sign up for verified certs?
if(verified):
# If the user is allowed a refund, do so
if has_access(user, course, 'refund'):
subject = _("[Refund] User-Requested Refund")
# todo: make this reference templates/student/refund_email.html
message = "Important info here."
to_email = [settings.PAYMENT_SUPPORT_EMAIL]
from_email = "support@edx.org"
try:
send_mail(subject, message, from_email, to_email, fail_silently=False)
except:
log.warning('Unable to send reimbursement request to billing', exc_info=True)
js['value'] = _('Could not send reimbursement request.')
return HttpResponse(json.dumps(js))
# email has been sent, let's deal with the order now
CertificateItem.refund_cert(user, course_id)
CourseEnrollment.unenroll(user, course_id) CourseEnrollment.unenroll(user, course_id)
org, course_num, run = course_id.split("/") org, course_num, run = course_id.split("/")
...@@ -473,10 +509,7 @@ def change_enrollment(request): ...@@ -473,10 +509,7 @@ def change_enrollment(request):
"course:{0}".format(course_num), "course:{0}".format(course_num),
"run:{0}".format(run)] "run:{0}".format(run)]
) )
return HttpResponse() return HttpResponse()
except CourseEnrollment.DoesNotExist:
return HttpResponseBadRequest(_("You are not enrolled in this course"))
else: else:
return HttpResponseBadRequest(_("Enrollment action is invalid")) return HttpResponseBadRequest(_("Enrollment action is invalid"))
...@@ -891,7 +924,7 @@ def create_account(request, post_override=None): ...@@ -891,7 +924,7 @@ def create_account(request, post_override=None):
subject = ''.join(subject.splitlines()) subject = ''.join(subject.splitlines())
message = render_to_string('emails/activation_email.txt', d) message = render_to_string('emails/activation_email.txt', d)
# dont send email if we are doing load testing or random user generation for some reason # don't send email if we are doing load testing or random user generation for some reason
if not (settings.MITX_FEATURES.get('AUTOMATIC_AUTH_FOR_TESTING')): if not (settings.MITX_FEATURES.get('AUTOMATIC_AUTH_FOR_TESTING')):
try: try:
if settings.MITX_FEATURES.get('REROUTE_ACTIVATION_EMAIL'): if settings.MITX_FEATURES.get('REROUTE_ACTIVATION_EMAIL'):
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
Ideally, it will be the only place that needs to know about any special settings Ideally, it will be the only place that needs to know about any special settings
like DISABLE_START_DATES""" like DISABLE_START_DATES"""
import logging import logging
from datetime import datetime, timedelta from datetime import datetime, timedelta, date
from functools import partial from functools import partial
from django.conf import settings from django.conf import settings
...@@ -202,11 +202,33 @@ def _has_access_course_desc(user, course, action): ...@@ -202,11 +202,33 @@ def _has_access_course_desc(user, course, action):
return can_enroll() or can_load() return can_enroll() or can_load()
def can_refund():
"""
For paid/verified certificates, students may receive a refund IFF the deadline
for refunds has not yet passed. Note that this function *only* checks whether
or not that deadline has passed; checking whether the student actually *purchased*
a paid/verified certificate must be done elsewhere.
"""
now = datetime.now(UTC())
course_start = course.enrollment_start
# If there *is* no start date, user can be refunded
if course_start is None:
return True
# Presently, refunds are only allowed up to two weeks after the course
# start date.
grace_period = timedelta(days=14)
refund_end = course_start + grace_period
if (now.date() <= refund_end.date()):
return True
return False
checkers = { checkers = {
'load': can_load, 'load': can_load,
'load_forum': can_load_forum, 'load_forum': can_load_forum,
'enroll': can_enroll, 'enroll': can_enroll,
'see_exists': see_exists, 'see_exists': see_exists,
'refund': can_refund,
'staff': lambda: _has_staff_access_to_descriptor(user, course), 'staff': lambda: _has_staff_access_to_descriptor(user, course),
'instructor': lambda: _has_instructor_access_to_descriptor(user, course), 'instructor': lambda: _has_instructor_access_to_descriptor(user, course),
} }
......
...@@ -106,3 +106,20 @@ class AccessTestCase(TestCase): ...@@ -106,3 +106,20 @@ class AccessTestCase(TestCase):
# TODO: # TODO:
# Non-staff cannot enroll outside the open enrollment period if not specifically allowed # Non-staff cannot enroll outside the open enrollment period if not specifically allowed
def test__has_access_refund(self):
u = Mock()
today = datetime.datetime.now(UTC())
grace_period = datetime.timedelta(days=14)
one_day_extra = datetime.timedelta(days=1)
# User is allowed to receive refund if it is within two weeks of course start date
c = Mock(enrollment_start=(today-one_day_extra), id='edX/tests/Whenever')
self.assertTrue(access._has_access_course_desc(u, c, 'refund'))
c = Mock(enrollment_start=(today-grace_period), id='edX/test/Whenever')
self.assertTrue(access._has_access_course_desc(u, c, 'refund'))
# After two weeks, user may no longer receive a refund
c = Mock(enrollment_start=(today-grace_period-one_day_extra), id='edX/test/Whenever')
self.assertFalse(access._has_access_course_desc(u, c, 'refund'))
...@@ -9,7 +9,7 @@ from boto.exception import BotoServerError # this is a super-class of SESError ...@@ -9,7 +9,7 @@ from boto.exception import BotoServerError # this is a super-class of SESError
from django.db import models from django.db import models
from django.conf import settings from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import (ObjectDoesNotExist, MultipleObjectsReturned)
from django.core.mail import send_mail from django.core.mail import send_mail
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
...@@ -22,7 +22,6 @@ from xmodule.modulestore.exceptions import ItemNotFoundError ...@@ -22,7 +22,6 @@ from xmodule.modulestore.exceptions import ItemNotFoundError
from course_modes.models import CourseMode from course_modes.models import CourseMode
from mitxmako.shortcuts import render_to_string from mitxmako.shortcuts import render_to_string
from student.views import course_from_id
from student.models import CourseEnrollment from student.models import CourseEnrollment
from verify_student.models import SoftwareSecurePhotoVerification from verify_student.models import SoftwareSecurePhotoVerification
...@@ -34,13 +33,19 @@ log = logging.getLogger("shoppingcart") ...@@ -34,13 +33,19 @@ log = logging.getLogger("shoppingcart")
ORDER_STATUSES = ( ORDER_STATUSES = (
('cart', 'cart'), ('cart', 'cart'),
('purchased', 'purchased'), ('purchased', 'purchased'),
('refunded', 'refunded'), # Not used for now ('refunded', 'refunded'),
) )
# we need a tuple to represent the primary key of various OrderItem subclasses # we need a tuple to represent the primary key of various OrderItem subclasses
OrderItemSubclassPK = namedtuple('OrderItemSubclassPK', ['cls', 'pk']) # pylint: disable=C0103 OrderItemSubclassPK = namedtuple('OrderItemSubclassPK', ['cls', 'pk']) # pylint: disable=C0103
def course_from_id(course_id):
"""Return the CourseDescriptor corresponding to this course_id"""
course_loc = CourseDescriptor.id_to_location(course_id)
return modulestore().get_instance(course_id, course_loc)
class Order(models.Model): class Order(models.Model):
""" """
This is the model for an order. Before purchase, an Order and its related OrderItems are used This is the model for an order. Before purchase, an Order and its related OrderItems are used
...@@ -399,6 +404,23 @@ class CertificateItem(OrderItem): ...@@ -399,6 +404,23 @@ class CertificateItem(OrderItem):
mode = models.SlugField() mode = models.SlugField()
@classmethod @classmethod
def refund_cert(cls, target_user, target_course_id):
try:
target_cert = CertificateItem.objects.get(course_id=target_course_id, user_id=target_user, status='purchased', mode='verified')
target_cert.status = 'refunded'
# todo return success
return target_cert
except MultipleObjectsReturned:
# this seems like a thing that shouldn't happen
log.exception("Multiple entries for single verified cert found")
# but we can recover; select one item and refund it
# todo
except ObjectDoesNotExist:
# todo log properly
log.exception("No certificate found")
# handle the exception
@classmethod
@transaction.commit_on_success @transaction.commit_on_success
def add_to_order(cls, order, course_id, cost, mode, currency='usd'): def add_to_order(cls, order, course_id, cost, mode, currency='usd'):
""" """
......
...@@ -20,6 +20,7 @@ from student.tests.factories import UserFactory ...@@ -20,6 +20,7 @@ from student.tests.factories import UserFactory
from student.models import CourseEnrollment from student.models import CourseEnrollment
from course_modes.models import CourseMode from course_modes.models import CourseMode
from shoppingcart.exceptions import PurchasedCallbackException from shoppingcart.exceptions import PurchasedCallbackException
from django.core.exceptions import (ObjectDoesNotExist, MultipleObjectsReturned)
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE) @override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
...@@ -360,3 +361,27 @@ class CertificateItemTest(ModuleStoreTestCase): ...@@ -360,3 +361,27 @@ class CertificateItemTest(ModuleStoreTestCase):
cert_item = CertificateItem.add_to_order(cart, self.course_id, self.cost, 'honor') cert_item = CertificateItem.add_to_order(cart, self.course_id, self.cost, 'honor')
self.assertEquals(cert_item.single_item_receipt_template, self.assertEquals(cert_item.single_item_receipt_template,
'shoppingcart/receipt.html') 'shoppingcart/receipt.html')
def test_refund_cert_single_cert(self):
# enroll and buy; dup from test_existing_enrollment
CourseEnrollment.enroll(self.user, self.course_id)
cart = Order.get_cart_for_user(user=self.user)
CertificateItem.add_to_order(cart, self.course_id, self.cost, 'verified')
cart.purchase()
enrollment = CourseEnrollment.objects.get(user=self.user, course_id=self.course_id)
# now that it's there, let's try refunding it
order = CertificateItem.refund_cert(target_user=self.user, target_course_id=self.course_id)
self.assertEquals(order.status, 'refunded')
def test_refund_cert_no_cert_exists(self):
order = CertificateItem.refund_cert(target_user=self.user, target_course_id=self.course_id)
self.assertRaises(ObjectDoesNotExist)
def test_refund_cert_duplicate_certs_exist(self):
for i in range(0, 2):
CourseEnrollment.enroll(self.user, self.course_id)
cart = Order.get_cart_for_user(user=self.user)
CertificateItem.add_to_order(cart, self.course_id, self.cost, 'verified')
cart.purchase()
enrollment = CourseEnrollment.objects.get(user=self.user, course_id=self.course_id)
self.assertRaises(MultipleObjectsReturned)
...@@ -20,6 +20,7 @@ USE_I18N = True ...@@ -20,6 +20,7 @@ USE_I18N = True
TEMPLATE_DEBUG = True TEMPLATE_DEBUG = True
MITX_FEATURES['ENABLE_PAYMENT_FAKE'] = True
MITX_FEATURES['DISABLE_START_DATES'] = False MITX_FEATURES['DISABLE_START_DATES'] = False
MITX_FEATURES['ENABLE_SQL_TRACKING_LOGS'] = True MITX_FEATURES['ENABLE_SQL_TRACKING_LOGS'] = True
MITX_FEATURES['SUBDOMAIN_COURSE_LISTINGS'] = False # Enable to test subdomains--otherwise, want all courses to show up MITX_FEATURES['SUBDOMAIN_COURSE_LISTINGS'] = False # Enable to test subdomains--otherwise, want all courses to show up
...@@ -269,7 +270,7 @@ if SEGMENT_IO_LMS_KEY: ...@@ -269,7 +270,7 @@ if SEGMENT_IO_LMS_KEY:
CC_PROCESSOR['CyberSource']['SHARED_SECRET'] = os.environ.get('CYBERSOURCE_SHARED_SECRET', '') CC_PROCESSOR['CyberSource']['SHARED_SECRET'] = os.environ.get('CYBERSOURCE_SHARED_SECRET', '')
CC_PROCESSOR['CyberSource']['MERCHANT_ID'] = os.environ.get('CYBERSOURCE_MERCHANT_ID', '') CC_PROCESSOR['CyberSource']['MERCHANT_ID'] = os.environ.get('CYBERSOURCE_MERCHANT_ID', '')
CC_PROCESSOR['CyberSource']['SERIAL_NUMBER'] = os.environ.get('CYBERSOURCE_SERIAL_NUMBER', '') CC_PROCESSOR['CyberSource']['SERIAL_NUMBER'] = os.environ.get('CYBERSOURCE_SERIAL_NUMBER', '')
CC_PROCESSOR['CyberSource']['PURCHASE_ENDPOINT'] = os.environ.get('CYBERSOURCE_PURCHASE_ENDPOINT', '') CC_PROCESSOR['CyberSource']['PURCHASE_ENDPOINT'] = '/shoppingcart/payment_fake/'
########################## USER API ######################## ########################## USER API ########################
......
...@@ -190,7 +190,8 @@ ...@@ -190,7 +190,8 @@
<% cert_status = cert_statuses.get(course.id) %> <% cert_status = cert_statuses.get(course.id) %>
<% show_email_settings = (course.id in show_email_settings_for) %> <% show_email_settings = (course.id in show_email_settings_for) %>
<% course_mode_info = all_course_modes.get(course.id) %> <% course_mode_info = all_course_modes.get(course.id) %>
<%include file='dashboard/_dashboard_course_listing.html' args="course=course, enrollment=enrollment, show_courseware_link=show_courseware_link, cert_status=cert_status, show_email_settings=show_email_settings, course_mode_info=course_mode_info" /> <% show_refund_option = (course.id in show_refund_option_for) %>
<%include file='dashboard/dashboard_course_listing.html' args="course=course, enrollment=enrollment, show_courseware_link=show_courseware_link, cert_status=cert_status, show_email_settings=show_email_settings, course_mode_info=course_mode_info, show_refund_option = show_refund_option" />
% endfor % endfor
</ul> </ul>
...@@ -244,26 +245,7 @@ ...@@ -244,26 +245,7 @@
</div> </div>
</section> </section>
<section id="unenroll-modal" class="modal unenroll-modal" aria-hidden="true">
<div class="inner-wrapper" role="alertdialog" aria-labelledy="unenrollment-modal-title">
<button class="close-modal">&#10005; <span class="sr">${_('Close Modal')}</span></button>
<header>
<h2 id="unenrollment-modal-title">${_('Are you sure you want to unregister from {course_number}?').format(course_number='<span id="unenroll_course_number"></span>')}<span class="sr">, ${_("modal open")}</span></h2>
<hr/>
</header>
<div id="unenroll_error" class="modal-form-error"></div>
<form id="unenroll_form" method="post" data-remote="true" action="${reverse('change_enrollment')}">
<input name="course_id" id="unenroll_course_id" type="hidden" />
<input name="enrollment_action" type="hidden" value="unenroll" />
<div class="submit">
<input name="submit" type="submit" value="${_('Unregister')}" />
</div>
</form>
</div>
</section>
<section id="password_reset_complete" class="modal" aria-hidden="true"> <section id="password_reset_complete" class="modal" aria-hidden="true">
<div class="inner-wrapper" role="dialog" aria-labelledby="password-reset-email"> <div class="inner-wrapper" role="dialog" aria-labelledby="password-reset-email">
......
<%page args="course, enrollment, show_courseware_link, cert_status, show_email_settings, course_mode_info" /> <%page args="course, enrollment, show_courseware_link, cert_status, show_email_settings, course_mode_info, show_refund_option" />
<%! from django.utils.translation import ugettext as _ %> <%! from django.utils.translation import ugettext as _ %>
<%! <%!
...@@ -143,10 +143,41 @@ ...@@ -143,10 +143,41 @@
<a href="#unenroll-modal" class="unenroll" rel="leanModal" data-course-id="${course.id}" data-course-number="${course.number}">${_('Unregister')}</a> <a href="#unenroll-modal" class="unenroll" rel="leanModal" data-course-id="${course.id}" data-course-number="${course.number}">${_('Unregister')}</a>
% if show_email_settings: % if show_email_settings:
<a href="#email-settings-modal" class="email-settings" rel="leanModal" data-course-id="${course.id}" data-course-number="${course.number}" data-optout="${course.id in course_optouts}">${_('Email Settings')}</a> <a href="#email-settings-modal" class="email-settings" rel="leanModal" data-course-id="${course.id}" data-course-number="${course.number}" data-optout="${course.id in course_optouts}">${_('Email Settings')}</a>
% endif % endif
</section> </section>
</article> </article>
</li> </li>
<section id="unenroll-modal" class="modal unenroll-modal" aria-hidden="true">
<div class="inner-wrapper" role="alertdialog" aria-labelledy="unenrollment-modal-title">
<button class="close-modal">&#10005; <span class="sr">${_('Close Modal')}</span></button>
<header>
% if enrollment.mode != "verified":
<h2 id="unenrollment-modal-title">${_('Are you sure you want to unregister from {course_number}?').format(course_number='<span id="unenroll_course_number"></span>')}<span class="sr">, ${_("modal open")}</span></h2>
% elif show_refund_option:
<h2 id="unenrollment-modal-title">${_('Are you sure you want to unregister from {course_number}? You will be refunded for the amount paid for the verified certificate.').format(course_number='<span id="unenroll_course_number"></span>')}<span class="sr">, ${_("modal open")}</span></h2>
% else:
<h2 id="unenrollment-modal-title">${_('Are you sure you want to unregister from {course_number}? The deadline for verified certificate refunds has passed, so you will not receive any money back.').format(course_number='<span id="unenroll_course_number"></span>')}<span class="sr">, ${_("modal open")}</span></h2>
% endif
<hr/>
</header>
<div id="unenroll_error" class="modal-form-error"></div>
<form id="unenroll_form" method="post" data-remote="true" action="${reverse('change_enrollment')}">
<input name="course_id" id="unenroll_course_id" type="hidden" />
<input name="enrollment_action" type="hidden" value="unenroll" />
<div class="submit">
<input name="submit" type="submit" value="${_('Unregister')}" />
</div>
</form>
</div>
</section>
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