Studio homepage escaping
This commit is contained in:
@@ -9,6 +9,9 @@ from mock import patch, Mock
|
||||
import ddt
|
||||
|
||||
from django.test import RequestFactory
|
||||
from django.test.client import Client
|
||||
|
||||
from common.test.utils import XssTestMixin
|
||||
from xmodule.course_module import CourseSummary
|
||||
|
||||
from contentstore.views.course import (_accessible_courses_list, _accessible_courses_list_from_groups,
|
||||
@@ -30,7 +33,7 @@ USER_COURSES_COUNT = 50
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class TestCourseListing(ModuleStoreTestCase):
|
||||
class TestCourseListing(ModuleStoreTestCase, XssTestMixin):
|
||||
"""
|
||||
Unit tests for getting the list of courses for a logged in user
|
||||
"""
|
||||
@@ -72,6 +75,30 @@ class TestCourseListing(ModuleStoreTestCase):
|
||||
self.client.logout()
|
||||
ModuleStoreTestCase.tearDown(self)
|
||||
|
||||
def test_course_listing_is_escaped(self):
|
||||
"""
|
||||
Tests course listing returns escaped data.
|
||||
"""
|
||||
escaping_content = "<script>alert('ESCAPE')</script>"
|
||||
|
||||
# Make user staff to access course listing
|
||||
self.user.is_staff = True
|
||||
self.user.save() # pylint: disable=no-member
|
||||
|
||||
self.client = Client()
|
||||
self.client.login(username=self.user.username, password='test')
|
||||
|
||||
# Change 'display_coursenumber' field and update the course.
|
||||
course = CourseFactory.create()
|
||||
course.display_coursenumber = escaping_content
|
||||
course = self.store.update_item(course, self.user.id) # pylint: disable=no-member
|
||||
self.assertEqual(course.display_coursenumber, escaping_content)
|
||||
|
||||
# Check if response is escaped
|
||||
response = self.client.get('/home')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assert_no_xss(response, escaping_content)
|
||||
|
||||
def test_get_course_list(self):
|
||||
"""
|
||||
Test getting courses with new access group format e.g. 'instructor_edx.course.run'
|
||||
|
||||
@@ -10,6 +10,7 @@ from provider.constants import CONFIDENTIAL
|
||||
|
||||
from openedx.core.djangoapps.programs.models import ProgramsApiConfig
|
||||
from openedx.core.djangoapps.programs.tests.mixins import ProgramsApiConfigMixin, ProgramsDataMixin
|
||||
from openedx.core.djangolib.markup import escape
|
||||
from student.tests.factories import UserFactory
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
|
||||
@@ -63,7 +64,7 @@ class TestProgramListing(ProgramsApiConfigMixin, ProgramsDataMixin, SharedModule
|
||||
self.mock_programs_api(data={'results': []})
|
||||
|
||||
response = self.client.get(self.studio_home)
|
||||
self.assertIn("You haven't created any programs yet.", response.content)
|
||||
self.assertIn(escape("You haven't created any programs yet."), response.content)
|
||||
|
||||
# When data is provided, expect a program listing.
|
||||
self.mock_programs_api()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<%! from django.utils.translation import ugettext as _ %>
|
||||
<%! from openedx.core.djangolib.markup import HTML, ugettext as _ %>
|
||||
<%page expression_filter="h"/>
|
||||
|
||||
<%inherit file="base.html" />
|
||||
|
||||
@@ -79,7 +80,10 @@
|
||||
## Translators: This is an example for the name of the organization sponsoring a course, seen when filling out the form to create a new course. The organization name cannot contain spaces.
|
||||
## Translators: "e.g. UniversityX or OrganizationX" is a placeholder displayed when user put no data into this field.
|
||||
<input class="new-course-org" id="new-course-org" type="text" name="new-course-org" required placeholder="${_('e.g. UniversityX or OrganizationX')}" aria-describedby="tip-new-course-org tip-error-new-course-org" />
|
||||
<span class="tip" id="tip-new-course-org">${_("The name of the organization sponsoring the course.")} <strong>${_("Note: The organization name is part of the course URL")}</strong> ${_("This cannot be changed, but you can set a different display name in Advanced Settings later.")}</span>
|
||||
<span class="tip" id="tip-new-course-org">${_("The name of the organization sponsoring the course. {strong_start}Note: The organization name is part of the course URL.{strong_end} This cannot be changed, but you can set a different display name in Advanced Settings later.").format(
|
||||
strong_start=HTML('<strong>'),
|
||||
strong_end=HTML('</strong>'),
|
||||
)}</span>
|
||||
<span class="tip tip-error is-hiding" id="tip-error-new-course-org"></span>
|
||||
</li>
|
||||
|
||||
@@ -89,7 +93,10 @@
|
||||
## seen when filling out the form to create a new course. The number here is
|
||||
## short for "Computer Science 101". It can contain letters but cannot contain spaces.
|
||||
<input class="new-course-number" id="new-course-number" type="text" name="new-course-number" required placeholder="${_('e.g. CS101')}" aria-describedby="tip-new-course-number tip-error-new-course-number" />
|
||||
<span class="tip" id="tip-new-course-number">${_("The unique number that identifies your course within your organization.")} <strong>${_("Note: This is part of your course URL, so no spaces or special characters are allowed and it cannot be changed.")}</strong></span>
|
||||
<span class="tip" id="tip-new-course-number">${_("The unique number that identifies your course within your organization. {strong_start}Note: This is part of your course URL, so no spaces or special characters are allowed and it cannot be changed.{strong_end}").format(
|
||||
strong_start=HTML('<strong>'),
|
||||
strong_end=HTML('</strong>'),
|
||||
)}</span>
|
||||
<span class="tip tip-error is-hiding" id="tip-error-new-course-number"></span>
|
||||
</li>
|
||||
|
||||
@@ -98,7 +105,10 @@
|
||||
## Translators: This is an example for the "run" used to identify different
|
||||
## instances of a course, seen when filling out the form to create a new course.
|
||||
<input class="new-course-run" id="new-course-run" type="text" name="new-course-run" required placeholder="${_('e.g. 2014_T1')}" aria-describedby="tip-new-course-run tip-error-new-course-run" />
|
||||
<span class="tip" id="tip-new-course-run">${_("The term in which your course will run.")} <strong>${_("Note: This is part of your course URL, so no spaces or special characters are allowed and it cannot be changed.")}</strong></span>
|
||||
<span class="tip" id="tip-new-course-run">${_("The term in which your course will run. {strong_start}Note: This is part of your course URL, so no spaces or special characters are allowed and it cannot be changed.{strong_end}").format(
|
||||
strong_start=HTML('<strong>'),
|
||||
strong_end=HTML('</strong>'),
|
||||
)}</span>
|
||||
<span class="tip tip-error is-hiding" id="tip-error-new-course-run"></span>
|
||||
</li>
|
||||
</ol>
|
||||
@@ -155,7 +165,10 @@
|
||||
## for "Computer Science Problems". The example number may contain letters
|
||||
## but must not contain spaces.
|
||||
<input class="new-library-number" id="new-library-number" type="text" name="new-library-number" required placeholder="${_('e.g. CSPROB')}" aria-describedby="tip-new-library-number tip-error-new-library-number" />
|
||||
<span class="tip" id="tip-new-library-number">${_("The unique code that identifies this library.")} <strong>${_("Note: This is part of your library URL, so no spaces or special characters are allowed.")}</strong> ${_("This cannot be changed.")}</span>
|
||||
<span class="tip" id="tip-new-library-number">${_("The unique code that identifies this library. {strong_start}Note: This is part of your library URL, so no spaces or special characters are allowed.{strong_end} This cannot be changed.").format(
|
||||
strong_start=HTML('<strong>'),
|
||||
strong_end=HTML('</strong>'),
|
||||
)}</span>
|
||||
<span class="tip tip-error is-hiding" id="tip-error-new-library-number"></span>
|
||||
</li>
|
||||
</ol>
|
||||
@@ -181,10 +194,10 @@
|
||||
%for course_info in sorted(in_process_course_actions, key=lambda s: s['display_name'].lower() if s['display_name'] is not None else ''):
|
||||
<!-- STATE: re-run is processing -->
|
||||
%if course_info['is_in_progress']:
|
||||
<li class="wrapper-course has-status" data-course-key="${course_info['course_key'] | h}">
|
||||
<li class="wrapper-course has-status" data-course-key="${course_info['course_key']}">
|
||||
<div class="course-item course-rerun is-processing">
|
||||
<div class="course-details" href="#">
|
||||
<h3 class="course-title">${course_info['display_name'] | h}</h3>
|
||||
<h3 class="course-title">${course_info['display_name']}</h3>
|
||||
|
||||
<div class="course-metadata">
|
||||
<span class="course-org metadata-item">
|
||||
@@ -216,8 +229,8 @@
|
||||
|
||||
<div class="status-message">
|
||||
<p class="copy">${_('The new course will be added to your course list in 5-10 minutes. Return to this page or {link_start}refresh it{link_end} to update the course list. The new course will need some manual configuration.').format(
|
||||
link_start='<a href="#" class="action-reload">',
|
||||
link_end='</a>',
|
||||
link_start=HTML('<a href="#" class="action-reload">'),
|
||||
link_end=HTML('</a>'),
|
||||
)}</p>
|
||||
</div>
|
||||
</li>
|
||||
@@ -227,10 +240,10 @@
|
||||
|
||||
<!-- STATE: re-run has error -->
|
||||
%if course_info['is_failed']:
|
||||
<li class="wrapper-course has-status" data-course-key="${course_info['course_key'] | h}">
|
||||
<li class="wrapper-course has-status" data-course-key="${course_info['course_key']}">
|
||||
<div class="course-item course-rerun has-error">
|
||||
<div class="course-details" href="#">
|
||||
<h3 class="course-title">${course_info['display_name'] | h}</h3>
|
||||
<h3 class="course-title">${course_info['display_name']}</h3>
|
||||
|
||||
<div class="course-metadata">
|
||||
<span class="course-org metadata-item">
|
||||
@@ -296,9 +309,9 @@
|
||||
<div class="courses courses-tab active">
|
||||
<ul class="list-courses">
|
||||
%for course_info in sorted(courses, key=lambda s: s['display_name'].lower() if s['display_name'] is not None else ''):
|
||||
<li class="course-item" data-course-key="${course_info['course_key'] | h}">
|
||||
<li class="course-item" data-course-key="${course_info['course_key']}">
|
||||
<a class="course-link" href="${course_info['url']}">
|
||||
<h3 class="course-title">${course_info['display_name'] | h}</h3>
|
||||
<h3 class="course-title">${course_info['display_name']}</h3>
|
||||
|
||||
<div class="course-metadata">
|
||||
<span class="course-org metadata-item">
|
||||
@@ -450,7 +463,7 @@
|
||||
%for library_info in sorted(libraries, key=lambda s: s['display_name'].lower() if s['display_name'] is not None else ''):
|
||||
<li class="course-item">
|
||||
<a class="library-link" href="${library_info['url']}">
|
||||
<h3 class="course-title">${library_info['display_name'] | h}</h3>
|
||||
<h3 class="course-title">${library_info['display_name']}</h3>
|
||||
|
||||
<div class="course-metadata">
|
||||
<span class="course-org metadata-item">
|
||||
@@ -508,12 +521,12 @@
|
||||
<li class="course-item">
|
||||
|
||||
<a class="program-link" href=${program_authoring_url + str(program['id'])}>
|
||||
<h3 class="course-title">${program['name'] | h}</h3>
|
||||
<h3 class="course-title">${program['name']}</h3>
|
||||
|
||||
<div class="course-metadata">
|
||||
<span class="course-org metadata-item">
|
||||
<!-- As of this writing, programs can only be owned by one organization. If that constraint is relaxed, this will need to be revisited. -->
|
||||
<span class="label">${_("Organization:")}</span> <span class="value">${program['organizations'][0]['key'] | h}</span>
|
||||
<span class="label">${_("Organization:")}</span> <span class="value">${program['organizations'][0]['key']}</span>
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
@@ -565,8 +578,8 @@
|
||||
<p>${_("In order to create courses in {studio_name}, you must {link_start}contact {platform_name} staff to help you create a course{link_end}.").format(
|
||||
studio_name=settings.STUDIO_NAME,
|
||||
platform_name=settings.PLATFORM_NAME,
|
||||
link_start='<a href="mailto:{email}">'.format(email=settings.FEATURES.get('STUDIO_REQUEST_EMAIL','')),
|
||||
link_end="</a>",
|
||||
link_start=HTML('<a href="mailto:{email}">').format(email=settings.FEATURES.get('STUDIO_REQUEST_EMAIL','')),
|
||||
link_end=HTML("</a>"),
|
||||
)}</p>
|
||||
</div>
|
||||
% endif
|
||||
@@ -580,17 +593,11 @@
|
||||
% elif course_creator_status == "denied":
|
||||
<div class="bit">
|
||||
<h3 class="title title-3">${_("Can I create courses in {studio_name}?").format(studio_name=settings.STUDIO_NAME)}</h3>
|
||||
<%!
|
||||
from django.conf import settings
|
||||
|
||||
help_link_start = '<a href="mailto:{email}">'.format(email=settings.TECH_SUPPORT_EMAIL)
|
||||
help_link_end = '</a>'
|
||||
%>
|
||||
<p>${_("Your request to author courses in {studio_name} has been denied. Please {link_start}contact {platform_name} Staff with further questions{link_end}.").format(
|
||||
studio_name=settings.STUDIO_NAME,
|
||||
platform_name=settings.PLATFORM_NAME,
|
||||
link_start=help_link_start,
|
||||
link_end=help_link_end,
|
||||
link_start=HTML('<a href="mailto:{email}">').format(email=settings.TECH_SUPPORT_EMAIL),
|
||||
link_end=HTML('</a>'),
|
||||
)}</p>
|
||||
</div>
|
||||
|
||||
@@ -603,14 +610,14 @@ help_link_end = '</a>'
|
||||
<section class="content">
|
||||
<article class="content-primary" role="main">
|
||||
<div class="introduction">
|
||||
<h2 class="title">${_("Thanks for signing up, %(name)s!") % dict(name= user.username)}</h2>
|
||||
<h2 class="title">${_("Thanks for signing up, {name}!").format(name=user.username)}</h2>
|
||||
</div>
|
||||
|
||||
<div class="notice notice-incontext notice-instruction notice-instruction-verification">
|
||||
<div class="msg">
|
||||
<h3 class="title">${_("We need to verify your email address")}</h3>
|
||||
<div class="copy">
|
||||
<p>${_('Almost there! In order to complete your sign up we need you to verify your email address (%(email)s). An activation message and next steps should be waiting for you there.') % dict(email=user.email)}</p>
|
||||
<p>${_('Almost there! In order to complete your sign up we need you to verify your email address ({email}). An activation message and next steps should be waiting for you there.').format(email=user.email)}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user