Add financial assistance page.
Page skeleton and endpoint to submit FA requests to Zendesk. ECOM-2824 ECOM-3038
This commit is contained in:
@@ -8,7 +8,7 @@ import ddt
|
||||
import json
|
||||
import itertools
|
||||
import unittest
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timedelta
|
||||
from HTMLParser import HTMLParser
|
||||
from nose.plugins.attrib import attr
|
||||
|
||||
@@ -32,6 +32,7 @@ from certificates import api as certs_api
|
||||
from certificates.models import CertificateStatuses, CertificateGenerationConfiguration
|
||||
from certificates.tests.factories import GeneratedCertificateFactory
|
||||
from course_modes.models import CourseMode
|
||||
from course_modes.tests.factories import CourseModeFactory
|
||||
from courseware.model_data import set_score
|
||||
from courseware.testutils import RenderXBlockTestMixin
|
||||
from courseware.tests.factories import StudentModuleFactory
|
||||
@@ -190,7 +191,8 @@ class ViewsTestCase(ModuleStoreTestCase):
|
||||
self.component = ItemFactory.create(category='problem', parent_location=self.vertical.location)
|
||||
|
||||
self.course_key = self.course.id
|
||||
self.user = UserFactory(username='dummy', password='123456', email='test@mit.edu')
|
||||
self.password = '123456'
|
||||
self.user = UserFactory(username='dummy', password=self.password, email='test@mit.edu')
|
||||
self.date = datetime(2013, 1, 22, tzinfo=UTC)
|
||||
self.enrollment = CourseEnrollment.enroll(self.user, self.course_key)
|
||||
self.enrollment.created = self.date
|
||||
@@ -270,7 +272,7 @@ class ViewsTestCase(ModuleStoreTestCase):
|
||||
self.section.location.name,
|
||||
'f'
|
||||
])
|
||||
self.client.login(username=self.user.username, password="123456")
|
||||
self.client.login(username=self.user.username, password=self.password)
|
||||
response = self.client.get(request_url)
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
@@ -283,7 +285,7 @@ class ViewsTestCase(ModuleStoreTestCase):
|
||||
self.section.location.name,
|
||||
'1'
|
||||
]
|
||||
self.client.login(username=self.user.username, password="123456")
|
||||
self.client.login(username=self.user.username, password=self.password)
|
||||
for idx, val in enumerate(url_parts):
|
||||
url_parts_copy = url_parts[:]
|
||||
url_parts_copy[idx] = val + u'χ'
|
||||
@@ -458,6 +460,136 @@ class ViewsTestCase(ModuleStoreTestCase):
|
||||
# Verify that the email opt-in checkbox does not appear
|
||||
self.assertNotContains(response, checkbox_html, html=True)
|
||||
|
||||
def test_financial_assistance_page(self):
|
||||
self.client.login(username=self.user.username, password=self.password)
|
||||
url = reverse('financial_assistance')
|
||||
response = self.client.get(url)
|
||||
# This is a static page, so just assert that it is returned correctly
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn('Financial Assistance Application', response.content)
|
||||
|
||||
def test_financial_assistance_form(self):
|
||||
non_verified_course = CourseFactory.create().id
|
||||
verified_course_verified_track = CourseFactory.create().id
|
||||
verified_course_audit_track = CourseFactory.create().id
|
||||
verified_course_deadline_passed = CourseFactory.create().id
|
||||
unenrolled_course = CourseFactory.create().id
|
||||
|
||||
enrollments = (
|
||||
(non_verified_course, CourseMode.AUDIT, None),
|
||||
(verified_course_verified_track, CourseMode.VERIFIED, None),
|
||||
(verified_course_audit_track, CourseMode.AUDIT, None),
|
||||
(verified_course_deadline_passed, CourseMode.AUDIT, datetime.now(UTC) - timedelta(days=1))
|
||||
)
|
||||
for course, mode, expiration in enrollments:
|
||||
CourseModeFactory(mode_slug=CourseMode.AUDIT, course_id=course)
|
||||
if course != non_verified_course:
|
||||
CourseModeFactory(mode_slug=CourseMode.VERIFIED, course_id=course, expiration_datetime=expiration)
|
||||
CourseEnrollmentFactory(course_id=course, user=self.user, mode=mode)
|
||||
|
||||
self.client.login(username=self.user.username, password=self.password)
|
||||
url = reverse('financial_assistance_form')
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Ensure that the user can only apply for assistance in
|
||||
# courses which have a verified mode which hasn't expired yet,
|
||||
# where the user is not already enrolled in verified mode
|
||||
self.assertIn(str(verified_course_audit_track), response.content)
|
||||
for course in (
|
||||
non_verified_course,
|
||||
verified_course_verified_track,
|
||||
verified_course_deadline_passed,
|
||||
unenrolled_course
|
||||
):
|
||||
self.assertNotIn(str(course), response.content)
|
||||
|
||||
def _submit_financial_assistance_form(self, data):
|
||||
"""Submit a financial assistance request."""
|
||||
self.client.login(username=self.user.username, password=self.password)
|
||||
url = reverse('submit_financial_assistance_request')
|
||||
return self.client.post(url, json.dumps(data), content_type='application/json')
|
||||
|
||||
@patch.object(views, '_record_feedback_in_zendesk')
|
||||
def test_submit_financial_assistance_request(self, mock_record_feedback):
|
||||
username = self.user.username
|
||||
course = unicode(self.course_key)
|
||||
legal_name = 'Jesse Pinkman'
|
||||
country = 'United States'
|
||||
income = '1234567890'
|
||||
reason_for_applying = "It's just basic chemistry, yo."
|
||||
goals = "I don't know if it even matters, but... work with my hands, I guess."
|
||||
effort = "I'm done, okay? You just give me my money, and you and I, we're done."
|
||||
data = {
|
||||
'username': username,
|
||||
'course_id': course,
|
||||
'legal_name': legal_name,
|
||||
'email': self.user.email,
|
||||
'country': country,
|
||||
'income': income,
|
||||
'reason_for_applying': reason_for_applying,
|
||||
'goals': goals,
|
||||
'effort': effort,
|
||||
'marketing_permission': False,
|
||||
}
|
||||
response = self._submit_financial_assistance_form(data)
|
||||
self.assertEqual(response.status_code, 204)
|
||||
|
||||
__, ___, ticket_subject, ticket_body, tags, additional_info = mock_record_feedback.call_args[0]
|
||||
for info in (country, income, reason_for_applying, goals, effort):
|
||||
self.assertIn(info, ticket_body)
|
||||
self.assertIn('This user HAS NOT allowed this content to be used for edX marketing purposes.', ticket_body)
|
||||
|
||||
self.assertEqual(
|
||||
ticket_subject,
|
||||
'Financial assistance request for user {username} in course {course}'.format(
|
||||
username=username,
|
||||
course=course
|
||||
)
|
||||
)
|
||||
self.assertDictContainsSubset(
|
||||
{
|
||||
'issue_type': 'Financial Assistance',
|
||||
'course_id': course
|
||||
},
|
||||
tags
|
||||
)
|
||||
self.assertIn('Client IP', additional_info)
|
||||
|
||||
@patch.object(views, '_record_feedback_in_zendesk', return_value=False)
|
||||
def test_zendesk_submission_failed(self, _mock_record_feedback):
|
||||
response = self._submit_financial_assistance_form({
|
||||
'username': self.user.username,
|
||||
'course_id': '',
|
||||
'legal_name': '',
|
||||
'email': '',
|
||||
'country': '',
|
||||
'income': '',
|
||||
'reason_for_applying': '',
|
||||
'goals': '',
|
||||
'effort': '',
|
||||
'marketing_permission': False,
|
||||
})
|
||||
self.assertEqual(response.status_code, 500)
|
||||
|
||||
@ddt.data(
|
||||
({}, 400),
|
||||
({'username': 'wwhite'}, 403)
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_submit_financial_assistance_errors(self, data, status):
|
||||
response = self._submit_financial_assistance_form(data)
|
||||
self.assertEqual(response.status_code, status)
|
||||
|
||||
def test_financial_assistance_login_required(self):
|
||||
for url in (
|
||||
reverse('financial_assistance'),
|
||||
reverse('financial_assistance_form'),
|
||||
reverse('submit_financial_assistance_request')
|
||||
):
|
||||
response = self.client.get(url)
|
||||
self.assertRedirects(response, reverse('signin_user') + '?next=' + url)
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
# setting TIME_ZONE_DISPLAYED_FOR_DEADLINES explicitly
|
||||
|
||||
@@ -3,8 +3,9 @@ Courseware views functions
|
||||
"""
|
||||
|
||||
import logging
|
||||
import urllib
|
||||
import json
|
||||
import textwrap
|
||||
import urllib
|
||||
|
||||
from datetime import datetime
|
||||
from django.utils.translation import ugettext as _
|
||||
@@ -16,15 +17,18 @@ from django.core.urlresolvers import reverse
|
||||
from django.contrib.auth.models import User, AnonymousUser
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.db import transaction
|
||||
from django.db.models import Q
|
||||
from django.utils.timezone import UTC
|
||||
from django.views.decorators.http import require_GET, require_POST, require_http_methods
|
||||
from django.http import Http404, HttpResponse, HttpResponseBadRequest
|
||||
from django.http import Http404, HttpResponse, HttpResponseBadRequest, HttpResponseForbidden
|
||||
from django.shortcuts import redirect
|
||||
from certificates import api as certs_api
|
||||
from edxmako.shortcuts import render_to_response, render_to_string, marketing_link
|
||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||
from django.views.decorators.cache import cache_control
|
||||
from ipware.ip import get_ip
|
||||
from markupsafe import escape
|
||||
from rest_framework import status
|
||||
|
||||
from courseware import grades
|
||||
from courseware.access import has_access, _adjust_start_date_for_beta_testers
|
||||
@@ -72,6 +76,7 @@ from shoppingcart.models import CourseRegistrationCode
|
||||
from shoppingcart.utils import is_shopping_cart_enabled
|
||||
from opaque_keys import InvalidKeyError
|
||||
from util.milestones_helpers import get_prerequisite_courses_display
|
||||
from util.views import _record_feedback_in_zendesk
|
||||
|
||||
from microsite_configuration import microsite
|
||||
from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
||||
@@ -1404,3 +1409,229 @@ def render_xblock(request, usage_key_string, check_if_enrolled=True):
|
||||
'xqa_server': settings.FEATURES.get('XQA_SERVER', 'http://your_xqa_server.com'),
|
||||
}
|
||||
return render_to_response('courseware/courseware-chromeless.html', context)
|
||||
|
||||
|
||||
# Translators: "percent_sign" is the symbol "%". "platform_name" is a
|
||||
# string identifying the name of this installation, such as "edX".
|
||||
FINANCIAL_ASSISTANCE_HEADER = _(
|
||||
'{platform_name} now offers financial assistance for learners who want to earn verified certificates but'
|
||||
' who may not be able to pay the Verified Certificate fee. Eligible learners receive 90{percent_sign} off'
|
||||
' the Verified Certificate fee for a course.\nTo apply for financial assistance, enroll in the'
|
||||
' audit track for a course that offers Verified Certificates, and then complete this application.'
|
||||
' Note that you must complete a separate application for each course you take.'
|
||||
).format(
|
||||
percent_sign="%",
|
||||
platform_name=settings.PLATFORM_NAME
|
||||
).split('\n')
|
||||
|
||||
|
||||
FA_INCOME_LABEL = _('Annual Income')
|
||||
FA_REASON_FOR_APPLYING_LABEL = _(
|
||||
'Tell us about your current financial situation, including any unusual circumstances.'
|
||||
)
|
||||
FA_GOALS_LABEL = _(
|
||||
'Tell us about your learning or professional goals. How will a Verified Certificate in'
|
||||
' this course help you achieve these goals?'
|
||||
)
|
||||
FA_EFFORT_LABEL = _(
|
||||
'Tell us about your plans for this course. What steps will you take to help you complete'
|
||||
' the course work a receive a certificate?'
|
||||
)
|
||||
FA_SHORT_ANSWER_INSTRUCTIONS = _('Use between 250 and 500 words or so in your response.')
|
||||
|
||||
|
||||
@login_required
|
||||
def financial_assistance(_request):
|
||||
"""Render the initial financial assistance page."""
|
||||
return render_to_response('financial-assistance/financial-assistance.html', {
|
||||
'header_text': FINANCIAL_ASSISTANCE_HEADER
|
||||
})
|
||||
|
||||
|
||||
@login_required
|
||||
@require_POST
|
||||
def financial_assistance_request(request):
|
||||
"""Submit a request for financial assistance to Zendesk."""
|
||||
try:
|
||||
data = json.loads(request.body)
|
||||
# Simple sanity check that the session belongs to the user
|
||||
# submitting an FA request
|
||||
username = data['username']
|
||||
if request.user.username != username:
|
||||
return HttpResponseForbidden()
|
||||
|
||||
course_id = data['course_id']
|
||||
legal_name = data['legal_name']
|
||||
email = data['email']
|
||||
country = data['country']
|
||||
income = data['income']
|
||||
reason_for_applying = data['reason_for_applying']
|
||||
goals = data['goals']
|
||||
effort = data['effort']
|
||||
marketing_permission = data['marketing_permission']
|
||||
ip_address = get_ip(request)
|
||||
except ValueError:
|
||||
# Thrown if JSON parsing fails
|
||||
return HttpResponseBadRequest('Could not parse request JSON.')
|
||||
except KeyError as err:
|
||||
# Thrown if fields are missing
|
||||
return HttpResponseBadRequest('The field {} is required.'.format(err.message))
|
||||
|
||||
ticket_body = textwrap.dedent(
|
||||
'''
|
||||
Annual Income: {income}
|
||||
Country: {country}
|
||||
|
||||
{reason_label}
|
||||
{separator}
|
||||
{reason_for_applying}
|
||||
|
||||
{goals_label}
|
||||
{separator}
|
||||
{goals}
|
||||
|
||||
{effort_label}
|
||||
{separator}
|
||||
{effort}
|
||||
|
||||
This user {allowed_for_marketing} allowed this content to be used for edX marketing purposes.
|
||||
'''.format(
|
||||
income=income,
|
||||
country=country,
|
||||
reason_label=FA_REASON_FOR_APPLYING_LABEL,
|
||||
reason_for_applying=reason_for_applying,
|
||||
goals_label=FA_GOALS_LABEL,
|
||||
goals=goals,
|
||||
effort_label=FA_EFFORT_LABEL,
|
||||
effort=effort,
|
||||
allowed_for_marketing='HAS' if marketing_permission else 'HAS NOT',
|
||||
separator='=' * 16
|
||||
)
|
||||
)
|
||||
|
||||
zendesk_submitted = _record_feedback_in_zendesk(
|
||||
legal_name,
|
||||
email,
|
||||
'Financial assistance request for user {username} in course {course_id}'.format(
|
||||
username=username,
|
||||
course_id=course_id
|
||||
),
|
||||
ticket_body,
|
||||
{'issue_type': 'Financial Assistance', 'course_id': course_id},
|
||||
{'Client IP': ip_address}
|
||||
)
|
||||
|
||||
if not zendesk_submitted:
|
||||
# The call to Zendesk failed. The frontend will display a
|
||||
# message to the user.
|
||||
return HttpResponse(status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
return HttpResponse(status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
|
||||
@login_required
|
||||
def financial_assistance_form(request):
|
||||
"""Render the financial assistance application form page."""
|
||||
user = request.user
|
||||
enrolled_courses = [
|
||||
{'name': enrollment.course_overview.display_name, 'value': unicode(enrollment.course_id)}
|
||||
for enrollment in CourseEnrollment.enrollments_for_user(user).order_by('-created')
|
||||
if CourseMode.objects.filter(
|
||||
Q(expiration_datetime__isnull=True) | Q(expiration_datetime__gt=datetime.now(UTC())),
|
||||
course_id=enrollment.course_id,
|
||||
mode_slug=CourseMode.VERIFIED
|
||||
).exists()
|
||||
and enrollment.mode != CourseMode.VERIFIED
|
||||
]
|
||||
return render_to_response('financial-assistance/apply.html', {
|
||||
'header_text': FINANCIAL_ASSISTANCE_HEADER,
|
||||
'student_faq_url': marketing_link('FAQ'),
|
||||
'dashboard_url': reverse('dashboard'),
|
||||
'platform_name': settings.PLATFORM_NAME,
|
||||
'user_details': {
|
||||
'email': user.email,
|
||||
'username': user.username,
|
||||
'name': user.profile.name,
|
||||
'country': str(user.profile.country.name),
|
||||
},
|
||||
'submit_url': reverse('submit_financial_assistance_request'),
|
||||
'fields': [
|
||||
{
|
||||
'name': 'course',
|
||||
'type': 'select',
|
||||
'label': _('Course'),
|
||||
'placeholder': '',
|
||||
'defaultValue': '',
|
||||
'required': True,
|
||||
'options': enrolled_courses,
|
||||
'instructions': _(
|
||||
'Select the course for which you want to earn a verified certificate. If'
|
||||
' the course does not appear in the list, make sure that you have enrolled'
|
||||
' in the audit track for the course.'
|
||||
)
|
||||
},
|
||||
{
|
||||
'name': 'income',
|
||||
'type': 'text',
|
||||
'label': FA_INCOME_LABEL,
|
||||
'placeholder': _('income in USD ($)'),
|
||||
'defaultValue': '',
|
||||
'required': True,
|
||||
'restrictions': {},
|
||||
'instructions': _('Specify your annual income in USD.')
|
||||
},
|
||||
{
|
||||
'name': 'reason_for_applying',
|
||||
'type': 'textarea',
|
||||
'label': FA_REASON_FOR_APPLYING_LABEL,
|
||||
'placeholder': '',
|
||||
'defaultValue': '',
|
||||
'required': True,
|
||||
'restrictions': {
|
||||
'min_length': settings.FINANCIAL_ASSISTANCE_MIN_LENGTH,
|
||||
'max_length': settings.FINANCIAL_ASSISTANCE_MAX_LENGTH
|
||||
},
|
||||
'instructions': FA_SHORT_ANSWER_INSTRUCTIONS
|
||||
},
|
||||
{
|
||||
'name': 'goals',
|
||||
'type': 'textarea',
|
||||
'label': FA_GOALS_LABEL,
|
||||
'placeholder': '',
|
||||
'defaultValue': '',
|
||||
'required': True,
|
||||
'restrictions': {
|
||||
'min_length': settings.FINANCIAL_ASSISTANCE_MIN_LENGTH,
|
||||
'max_length': settings.FINANCIAL_ASSISTANCE_MAX_LENGTH
|
||||
},
|
||||
'instructions': FA_SHORT_ANSWER_INSTRUCTIONS
|
||||
},
|
||||
{
|
||||
'name': 'effort',
|
||||
'type': 'textarea',
|
||||
'label': FA_EFFORT_LABEL,
|
||||
'placeholder': '',
|
||||
'defaultValue': '',
|
||||
'required': True,
|
||||
'restrictions': {
|
||||
'min_length': settings.FINANCIAL_ASSISTANCE_MIN_LENGTH,
|
||||
'max_length': settings.FINANCIAL_ASSISTANCE_MAX_LENGTH
|
||||
},
|
||||
'instructions': FA_SHORT_ANSWER_INSTRUCTIONS
|
||||
},
|
||||
{
|
||||
'placeholder': '',
|
||||
'name': 'mktg-permission',
|
||||
'label': _(
|
||||
'I allow edX to use the information provided in this application for edX marketing purposes.'
|
||||
),
|
||||
'defaultValue': '',
|
||||
'type': 'checkbox',
|
||||
'required': False,
|
||||
'instructions': _(
|
||||
'Annual income and personal information such as email address will not be shared.'
|
||||
),
|
||||
'restrictions': {}
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
@@ -2732,3 +2732,10 @@ PROCTORING_SETTINGS = {}
|
||||
# The reason we introcuced this number is because we do not want the CCX
|
||||
# to compete with the MOOC.
|
||||
CCX_MAX_STUDENTS_ALLOWED = 200
|
||||
|
||||
# Financial assistance settings
|
||||
|
||||
# Maximum and minimum length of answers, in characters, for the
|
||||
# financial assistance form
|
||||
FINANCIAL_ASSISTANCE_MIN_LENGTH = 800
|
||||
FINANCIAL_ASSISTANCE_MAX_LENGTH = 2500
|
||||
|
||||
@@ -549,3 +549,6 @@ AUTHENTICATION_BACKENDS += ('lti_provider.users.LtiBackend',)
|
||||
|
||||
# ORGANIZATIONS
|
||||
FEATURES['ORGANIZATIONS_APP'] = True
|
||||
|
||||
# Financial assistance page
|
||||
FEATURES['ENABLE_FINANCIAL_ASSISTANCE_FORM'] = True
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
@import 'views/shoppingcart';
|
||||
@import 'views/homepage';
|
||||
@import 'views/support';
|
||||
@import "views/financial-assistance";
|
||||
@import 'course/auto-cert';
|
||||
|
||||
// app - discussion
|
||||
|
||||
76
lms/static/sass/views/_financial-assistance.scss
Normal file
76
lms/static/sass/views/_financial-assistance.scss
Normal file
@@ -0,0 +1,76 @@
|
||||
.financial-assistance-wrapper {
|
||||
margin: auto;
|
||||
padding: $baseline 0;
|
||||
max-width: 1180px;
|
||||
|
||||
.financial-assistance {
|
||||
border-bottom: 4px solid $gray-l5;
|
||||
|
||||
h1 {
|
||||
@extend %t-title4;
|
||||
@include text-align(left);
|
||||
margin: 0;
|
||||
padding: ($baseline/2) 0;
|
||||
border-bottom: 4px solid $gray-l5;
|
||||
color: $m-gray-d3;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@extend %t-title6;
|
||||
@extend %t-strong;
|
||||
margin-top: ($baseline/2);
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
p {
|
||||
@extend %t-copy-base;
|
||||
padding: ($baseline/2) 0;
|
||||
margin: 0;
|
||||
color: $m-gray-d2;
|
||||
}
|
||||
|
||||
.apply-form-list {
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
|
||||
.apply-form-section {
|
||||
border-bottom: 2px solid $gray-l5;
|
||||
}
|
||||
|
||||
.apply-form-section:last-child {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.about-me {
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
|
||||
.about-me-item {
|
||||
@include margin-right(150px);
|
||||
display: inline-block;
|
||||
|
||||
p {
|
||||
padding: 0;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.financial-assistance-footer {
|
||||
padding: $baseline;
|
||||
|
||||
.faq-link {
|
||||
padding: $baseline/2;
|
||||
}
|
||||
|
||||
.action-link {
|
||||
@include float(right);
|
||||
padding: $baseline/2;
|
||||
background-color: $m-blue-d2;
|
||||
color: $gray-l7;
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
lms/templates/financial-assistance/apply.html
Normal file
23
lms/templates/financial-assistance/apply.html
Normal file
@@ -0,0 +1,23 @@
|
||||
<%inherit file="../main.html"/>
|
||||
<%!
|
||||
import json
|
||||
|
||||
from openedx.core.lib.js_utils import escape_json_dumps
|
||||
%>
|
||||
<%namespace name='static' file='/static_content.html'/>
|
||||
|
||||
<%block name="js_extra">
|
||||
<%static:require_module module_name="js/financial-assistance/financial_assistance_form_factory" class_name="FinancialAssistanceFactory">
|
||||
FinancialAssistanceFactory({
|
||||
fields: ${escape_json_dumps(fields)},
|
||||
user_details: ${escape_json_dumps(user_details)},
|
||||
header_text: ${escape_json_dumps(header_text)},
|
||||
student_faq_url: ${json.dumps(student_faq_url)},
|
||||
dashboard_url: ${json.dumps(dashboard_url)},
|
||||
platform_name: ${escape_json_dumps(platform_name)},
|
||||
submit_url: ${json.dumps(submit_url)}
|
||||
});
|
||||
</%static:require_module>
|
||||
</%block>
|
||||
|
||||
<div class="financial-assistance-wrapper"></div>
|
||||
38
lms/templates/financial-assistance/financial-assistance.html
Normal file
38
lms/templates/financial-assistance/financial-assistance.html
Normal file
@@ -0,0 +1,38 @@
|
||||
<%inherit file="../main.html"/>
|
||||
<%
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from edxmako.shortcuts import marketing_link
|
||||
%>
|
||||
|
||||
<div class="financial-assistance-wrapper">
|
||||
|
||||
<div class="financial-assistance financial-assistance-header">
|
||||
<h1>${_("Financial Assistance Application")}</h1>
|
||||
% for line in header_text:
|
||||
<p>${line}</p>
|
||||
% endfor
|
||||
</div>
|
||||
|
||||
<div class="financial-assistance financial-assistance-body">
|
||||
<h2>${_("A Note to Learners")}</h2>
|
||||
<p>${_("Dear edX Learner,")}</p>
|
||||
<p>${_("EdX Financial Assistance is a program we created to give learners in all financial circumstances a chance to earn a Verified Certificate upon successful completion of an edX course.")}</p>
|
||||
<p>${_("If you are interested in working toward a Verified Certificate, but cannot afford to pay the fee, please apply now. Please note space is limited.")}</p>
|
||||
<p>${_("In order to be eligible for edX Financial Assistance, you must demonstrate that paying the Verified Certificate fee would cause you economic hardship. To apply, you will be asked to answer a few questions about why you are applying and how the Verified Certificate will benefit you.")}</p>
|
||||
<p>${_("Once your application is approved, we'll email to let you know and give you instructions for how to verify your identity on edX.org; then you can start working toward completing your edX course.")}</p>
|
||||
<p>${_("EdX is committed to making it possible for you to take high quality courses from leading institutions regardless of your financial situation, earn a Verified Certificate, and share your success with others.")}</p>
|
||||
<p class="signature">${_("Sincerely, Anant")}</p>
|
||||
</div>
|
||||
|
||||
<div class="financial-assistance-footer">
|
||||
<%
|
||||
faq_link = marketing_link('FAQ')
|
||||
%>
|
||||
% if faq_link != '#':
|
||||
<a class="faq-link" href="${faq_link}">${_("Back to Student FAQs")}</a>
|
||||
% endif
|
||||
<a class="action-link" href="${reverse('financial_assistance_form')}">${_("Apply for Financial Assistance")}</a>
|
||||
</div>
|
||||
</div>
|
||||
19
lms/urls.py
19
lms/urls.py
@@ -774,3 +774,22 @@ urlpatterns += (
|
||||
urlpatterns += (
|
||||
url(r'^api/', include('edx_proctoring.urls')),
|
||||
)
|
||||
|
||||
if settings.FEATURES.get('ENABLE_FINANCIAL_ASSISTANCE_FORM'):
|
||||
urlpatterns += (
|
||||
url(
|
||||
r'^financial-assistance/$',
|
||||
'courseware.views.financial_assistance',
|
||||
name='financial_assistance'
|
||||
),
|
||||
url(
|
||||
r'^financial-assistance/apply/$',
|
||||
'courseware.views.financial_assistance_form',
|
||||
name='financial_assistance_form'
|
||||
),
|
||||
url(
|
||||
r'^financial-assistance/submit/$',
|
||||
'courseware.views.financial_assistance_request',
|
||||
name='submit_financial_assistance_request'
|
||||
)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user