diff --git a/cms/djangoapps/contentstore/tests/test_contentstore.py b/cms/djangoapps/contentstore/tests/test_contentstore.py index d52330f5ff..9cb3b58933 100644 --- a/cms/djangoapps/contentstore/tests/test_contentstore.py +++ b/cms/djangoapps/contentstore/tests/test_contentstore.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- #pylint: disable=E1101 import json @@ -1423,6 +1424,15 @@ class ContentStoreTest(ModuleStoreTestCase): self.assertTrue(CourseEnrollment.is_enrolled(self.user, _get_course_id(test_course_data))) return test_course_data + def assert_create_course_failed(self, error_message): + """ + Checks that the course not created. + """ + resp = self.client.ajax_post('/course', self.course_data) + self.assertEqual(resp.status_code, 400) + data = parse_json(resp) + self.assertEqual(data['error'], error_message) + def test_create_course_check_forum_seeding(self): """Test new course creation and verify forum seeding """ test_course_data = self.assert_created_course(number_suffix=uuid4().hex) @@ -1562,6 +1572,21 @@ class ContentStoreTest(ModuleStoreTestCase): auth.add_users(self.user, CourseCreatorRole(), self.user) self.assert_created_course() + def test_create_course_with_unicode_in_id_disabled(self): + """ + Test new course creation with feature setting: ALLOW_UNICODE_COURSE_ID disabled. + """ + with mock.patch.dict('django.conf.settings.FEATURES', {'ALLOW_UNICODE_COURSE_ID': False}): + error_message = "Special characters not allowed in organization, course number, and course run." + self.course_data['org'] = u'Юникода' + self.assert_create_course_failed(error_message) + + self.course_data['number'] = u'échantillon' + self.assert_create_course_failed(error_message) + + self.course_data['run'] = u'όνομα' + self.assert_create_course_failed(error_message) + def assert_course_permission_denied(self): """ Checks that the course did not get created due to a PermissionError. @@ -2073,7 +2098,7 @@ def _course_factory_create_course(): def _get_course_id(test_course_data): """Returns the course ID (org/number/run).""" - return "{org}/{number}/{run}".format(**test_course_data) + return u"{org}/{number}/{run}".format(**test_course_data) def _test_no_locations(test, resp, status_code=200, html=True): diff --git a/cms/djangoapps/contentstore/views/course.py b/cms/djangoapps/contentstore/views/course.py index 6da30a1555..3e520a2434 100644 --- a/cms/djangoapps/contentstore/views/course.py +++ b/cms/djangoapps/contentstore/views/course.py @@ -36,6 +36,7 @@ from models.settings.course_details import CourseDetails, CourseSettingsEncoder from models.settings.course_grading import CourseGradingModel from models.settings.course_metadata import CourseMetadata from util.json_request import expect_json +from util.string_utils import _has_non_ascii_characters from .access import has_course_access from .tabs import initialize_course_tabs @@ -266,6 +267,7 @@ def course_listing(request): 'user': request.user, 'request_course_creator_url': reverse('contentstore.views.request_course_creator'), 'course_creator_status': _get_course_creator_status(request.user), + 'allow_unicode_course_id': settings.FEATURES.get('ALLOW_UNICODE_COURSE_ID', False) }) @@ -313,6 +315,14 @@ def create_new_course(request): display_name = request.json.get('display_name') run = request.json.get('run') + # allow/disable unicode characters in course_id according to settings + if not settings.FEATURES.get('ALLOW_UNICODE_COURSE_ID'): + if _has_non_ascii_characters(org) or _has_non_ascii_characters(number) or _has_non_ascii_characters(run): + return JsonResponse( + {'error': _('Special characters not allowed in organization, course number, and course run.')}, + status=400 + ) + try: dest_location = Location(u'i4x', org, number, u'course', run) except InvalidLocationError as error: diff --git a/cms/envs/common.py b/cms/envs/common.py index a5baa347e0..abf6210bec 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -86,6 +86,9 @@ FEATURES = { # Turn on/off Microsites feature 'USE_MICROSITES': False, + + # Allow creating courses with non-ascii characters in the course id + 'ALLOW_UNICODE_COURSE_ID': False, } ENABLE_JASMINE = False diff --git a/cms/static/js/index.js b/cms/static/js/index.js index fff1923f4d..0dc9e2b748 100644 --- a/cms/static/js/index.js +++ b/cms/static/js/index.js @@ -84,8 +84,15 @@ require(["domReady", "jquery", "underscore", "js/utils/cancel_on_escape"], if (required) { return required; } - if (/\s/g.test(item)) { - return gettext('Please do not use any spaces in this field.'); + if ($('.allow-unicode-course-id').val() === 'True'){ + if (/\s/g.test(item)) { + return gettext('Please do not use any spaces in this field.'); + } + } + else{ + if (item !== encodeURIComponent(item)) { + return gettext('Please do not use any spaces or special characters in this field.'); + } } return ''; }; diff --git a/cms/templates/index.html b/cms/templates/index.html index 787762f392..4c5a7518a8 100644 --- a/cms/templates/index.html +++ b/cms/templates/index.html @@ -123,6 +123,7 @@ require(["domReady!", "jquery", "jquery.form", "js/index"], function(doc, $) {