Commit 545cfd84 by Julia Hansbrough

Merge pull request #4353 from edx/flowerhack/lms-deprecated-urls

Enables LMS to handle deprecated URLs
parents 45195375 d9dd4566
...@@ -4,11 +4,11 @@ Unit tests for getting the list of courses and the course outline. ...@@ -4,11 +4,11 @@ Unit tests for getting the list of courses and the course outline.
import json import json
import lxml import lxml
from cms.urls import COURSE_KEY_PATTERN
from contentstore.tests.utils import CourseTestCase from contentstore.tests.utils import CourseTestCase
from contentstore.utils import reverse_course_url from contentstore.utils import reverse_course_url
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
from opaque_keys.edx.locator import Locator from opaque_keys.edx.locator import Locator
from django.conf import settings
class TestCourseIndex(CourseTestCase): class TestCourseIndex(CourseTestCase):
...@@ -39,7 +39,7 @@ class TestCourseIndex(CourseTestCase): ...@@ -39,7 +39,7 @@ class TestCourseIndex(CourseTestCase):
for link in course_link_eles: for link in course_link_eles:
self.assertRegexpMatches( self.assertRegexpMatches(
link.get("href"), link.get("href"),
'course/{}'.format(COURSE_KEY_PATTERN) 'course/{}'.format(settings.COURSE_KEY_PATTERN)
) )
# now test that url # now test that url
outline_response = authed_client.get(link.get("href"), {}, HTTP_ACCEPT='text/html') outline_response = authed_client.get(link.get("href"), {}, HTTP_ACCEPT='text/html')
......
...@@ -168,6 +168,12 @@ AUTHENTICATION_BACKENDS = ( ...@@ -168,6 +168,12 @@ AUTHENTICATION_BACKENDS = (
LMS_BASE = None LMS_BASE = None
# These are standard regexes for pulling out info like course_ids, usage_ids, etc.
# They are used so that URLs with deprecated-format strings still work.
from lms.envs.common import (
COURSE_KEY_PATTERN, COURSE_ID_PATTERN, USAGE_KEY_PATTERN, ASSET_KEY_PATTERN
)
#################### CAPA External Code Evaluation ############################# #################### CAPA External Code Evaluation #############################
XQUEUE_INTERFACE = { XQUEUE_INTERFACE = {
'url': 'http://localhost:8888', 'url': 'http://localhost:8888',
......
...@@ -5,10 +5,6 @@ from django.conf.urls import patterns, include, url ...@@ -5,10 +5,6 @@ from django.conf.urls import patterns, include, url
from ratelimitbackend import admin from ratelimitbackend import admin
admin.autodiscover() admin.autodiscover()
COURSE_KEY_PATTERN = r'(?P<course_key_string>(?:[^/]+/[^/]+/[^/]+)|(?:[^/]+))'
USAGE_KEY_PATTERN = r'(?P<usage_key_string>(?:i4x://?[^/]+/[^/]+/[^/]+/[^@]+(?:@[^/]+)?)|(?:[^/]+))'
ASSET_KEY_PATTERN = r'(?P<asset_key_string>(?:/?c4x(:/)?/[^/]+/[^/]+/[^/]+/[^@]+(?:@[^/]+)?)|(?:[^/]+))'
urlpatterns = patterns('', # nopep8 urlpatterns = patterns('', # nopep8
url(r'^transcripts/upload$', 'contentstore.views.upload_transcripts', name='upload_transcripts'), url(r'^transcripts/upload$', 'contentstore.views.upload_transcripts', name='upload_transcripts'),
...@@ -70,30 +66,30 @@ urlpatterns += patterns( ...@@ -70,30 +66,30 @@ urlpatterns += patterns(
url(r'^signin$', 'login_page', name='login'), url(r'^signin$', 'login_page', name='login'),
url(r'^request_course_creator$', 'request_course_creator'), url(r'^request_course_creator$', 'request_course_creator'),
url(r'^course_team/{}/(?P<email>.+)?$'.format(COURSE_KEY_PATTERN), 'course_team_handler'), url(r'^course_team/{}/(?P<email>.+)?$'.format(settings.COURSE_KEY_PATTERN), 'course_team_handler'),
url(r'^course_info/{}$'.format(COURSE_KEY_PATTERN), 'course_info_handler'), url(r'^course_info/{}$'.format(settings.COURSE_KEY_PATTERN), 'course_info_handler'),
url( url(
r'^course_info_update/{}/(?P<provided_id>\d+)?$'.format(COURSE_KEY_PATTERN), r'^course_info_update/{}/(?P<provided_id>\d+)?$'.format(settings.COURSE_KEY_PATTERN),
'course_info_update_handler' 'course_info_update_handler'
), ),
url(r'^course/{}?$'.format(COURSE_KEY_PATTERN), 'course_handler', name='course_handler'), url(r'^course/{}?$'.format(settings.COURSE_KEY_PATTERN), 'course_handler', name='course_handler'),
url(r'^subsection/{}$'.format(USAGE_KEY_PATTERN), 'subsection_handler'), url(r'^subsection/{}$'.format(settings.USAGE_KEY_PATTERN), 'subsection_handler'),
url(r'^unit/{}$'.format(USAGE_KEY_PATTERN), 'unit_handler'), url(r'^unit/{}$'.format(settings.USAGE_KEY_PATTERN), 'unit_handler'),
url(r'^container/{}$'.format(USAGE_KEY_PATTERN), 'container_handler'), url(r'^container/{}$'.format(settings.USAGE_KEY_PATTERN), 'container_handler'),
url(r'^checklists/{}/(?P<checklist_index>\d+)?$'.format(COURSE_KEY_PATTERN), 'checklists_handler'), url(r'^checklists/{}/(?P<checklist_index>\d+)?$'.format(settings.COURSE_KEY_PATTERN), 'checklists_handler'),
url(r'^orphan/{}$'.format(COURSE_KEY_PATTERN), 'orphan_handler'), url(r'^orphan/{}$'.format(settings.COURSE_KEY_PATTERN), 'orphan_handler'),
url(r'^assets/{}/{}?$'.format(COURSE_KEY_PATTERN, ASSET_KEY_PATTERN), 'assets_handler'), url(r'^assets/{}/{}?$'.format(settings.COURSE_KEY_PATTERN, settings.ASSET_KEY_PATTERN), 'assets_handler'),
url(r'^import/{}$'.format(COURSE_KEY_PATTERN), 'import_handler'), url(r'^import/{}$'.format(settings.COURSE_KEY_PATTERN), 'import_handler'),
url(r'^import_status/{}/(?P<filename>.+)$'.format(COURSE_KEY_PATTERN), 'import_status_handler'), url(r'^import_status/{}/(?P<filename>.+)$'.format(settings.COURSE_KEY_PATTERN), 'import_status_handler'),
url(r'^export/{}$'.format(COURSE_KEY_PATTERN), 'export_handler'), url(r'^export/{}$'.format(settings.COURSE_KEY_PATTERN), 'export_handler'),
url(r'^xblock/{}/(?P<view_name>[^/]+)$'.format(USAGE_KEY_PATTERN), 'xblock_view_handler'), url(r'^xblock/{}/(?P<view_name>[^/]+)$'.format(settings.USAGE_KEY_PATTERN), 'xblock_view_handler'),
url(r'^xblock/{}?$'.format(USAGE_KEY_PATTERN), 'xblock_handler'), url(r'^xblock/{}?$'.format(settings.USAGE_KEY_PATTERN), 'xblock_handler'),
url(r'^tabs/{}$'.format(COURSE_KEY_PATTERN), 'tabs_handler'), url(r'^tabs/{}$'.format(settings.COURSE_KEY_PATTERN), 'tabs_handler'),
url(r'^settings/details/{}$'.format(COURSE_KEY_PATTERN), 'settings_handler'), url(r'^settings/details/{}$'.format(settings.COURSE_KEY_PATTERN), 'settings_handler'),
url(r'^settings/grading/{}(/)?(?P<grader_index>\d+)?$'.format(COURSE_KEY_PATTERN), 'grading_handler'), url(r'^settings/grading/{}(/)?(?P<grader_index>\d+)?$'.format(settings.COURSE_KEY_PATTERN), 'grading_handler'),
url(r'^settings/advanced/{}$'.format(COURSE_KEY_PATTERN), 'advanced_settings_handler'), url(r'^settings/advanced/{}$'.format(settings.COURSE_KEY_PATTERN), 'advanced_settings_handler'),
url(r'^textbooks/{}$'.format(COURSE_KEY_PATTERN), 'textbooks_list_handler'), url(r'^textbooks/{}$'.format(settings.COURSE_KEY_PATTERN), 'textbooks_list_handler'),
url(r'^textbooks/{}/(?P<textbook_id>\d[^/]*)$'.format(COURSE_KEY_PATTERN), 'textbooks_detail_handler'), url(r'^textbooks/{}/(?P<textbook_id>\d[^/]*)$'.format(settings.COURSE_KEY_PATTERN), 'textbooks_detail_handler'),
) )
if settings.FEATURES.get('ENABLE_GROUP_CONFIGURATIONS'): if settings.FEATURES.get('ENABLE_GROUP_CONFIGURATIONS'):
...@@ -113,7 +109,7 @@ urlpatterns += patterns('', ...@@ -113,7 +109,7 @@ urlpatterns += patterns('',
if settings.FEATURES.get('ENABLE_EXPORT_GIT'): if settings.FEATURES.get('ENABLE_EXPORT_GIT'):
urlpatterns += (url(r'^export_git/{}$'.format(COURSE_KEY_PATTERN), urlpatterns += (url(r'^export_git/{}$'.format(settings.COURSE_KEY_PATTERN),
'contentstore.views.export_git', name='export_git'),) 'contentstore.views.export_git', name='export_git'),)
if settings.FEATURES.get('ENABLE_SERVICE_STATUS'): if settings.FEATURES.get('ENABLE_SERVICE_STATUS'):
......
from django.conf.urls import include, patterns, url from django.conf.urls import include, patterns, url
from django.conf import settings
from django.views.generic import TemplateView from django.views.generic import TemplateView
from course_modes import views from course_modes import views
urlpatterns = patterns( urlpatterns = patterns(
'', '',
url(r'^choose/(?P<course_id>[^/]+/[^/]+/[^/]+)/$', views.ChooseModeView.as_view(), name="course_modes_choose"), # pylint seems to dislike as_view() calls because it's a `classonlymethod` instead of `classmethod`, so we disable the warning
url(r'^choose/{}/$'.format(settings.COURSE_ID_PATTERN), views.ChooseModeView.as_view(), name="course_modes_choose"), # pylint: disable=no-value-for-parameter
) )
...@@ -87,6 +87,7 @@ from util.password_policy_validators import ( ...@@ -87,6 +87,7 @@ from util.password_policy_validators import (
from third_party_auth import pipeline, provider from third_party_auth import pipeline, provider
from xmodule.error_module import ErrorDescriptor from xmodule.error_module import ErrorDescriptor
log = logging.getLogger("edx.student") log = logging.getLogger("edx.student")
AUDIT_LOG = logging.getLogger("audit") AUDIT_LOG = logging.getLogger("audit")
...@@ -657,13 +658,16 @@ def change_enrollment(request): ...@@ -657,13 +658,16 @@ def change_enrollment(request):
return HttpResponseBadRequest(_("Enrollment action is invalid")) return HttpResponseBadRequest(_("Enrollment action is invalid"))
# TODO: This function is kind of gnarly/hackish/etc and is only used in one location.
# It'd be awesome if we could get rid of it; manually parsing course_id strings form larger strings
# seems Probably Incorrect
def _parse_course_id_from_string(input_str): def _parse_course_id_from_string(input_str):
""" """
Helper function to determine if input_str (typically the queryparam 'next') contains a course_id. Helper function to determine if input_str (typically the queryparam 'next') contains a course_id.
@param input_str: @param input_str:
@return: the course_id if found, None if not @return: the course_id if found, None if not
""" """
m_obj = re.match(r'^/courses/(?P<course_id>[^/]+/[^/]+/[^/]+)', input_str) m_obj = re.match(r'^/courses/{}'.format(settings.COURSE_ID_PATTERN), input_str)
if m_obj: if m_obj:
return SlashSeparatedCourseKey.from_deprecated_string(m_obj.group('course_id')) return SlashSeparatedCourseKey.from_deprecated_string(m_obj.group('course_id'))
return None return None
......
...@@ -5,7 +5,8 @@ from django.conf import settings ...@@ -5,7 +5,8 @@ from django.conf import settings
from microsite_configuration import microsite from microsite_configuration import microsite
from opaque_keys.edx.locations import SlashSeparatedCourseKey from opaque_keys.edx.locations import SlashSeparatedCourseKey
COURSE_REGEX = re.compile(r'^.*?/courses/(?P<course_id>[^/]+/[^/]+/[^/]+)')
COURSE_REGEX = re.compile(r'^.*?/courses/{}'.format(settings.COURSE_ID_PATTERN))
def safe_get_host(request): def safe_get_host(request):
......
...@@ -3,17 +3,19 @@ Class Dashboard API endpoint urls. ...@@ -3,17 +3,19 @@ Class Dashboard API endpoint urls.
""" """
from django.conf.urls import patterns, url from django.conf.urls import patterns, url
from django.conf import settings
COURSE_ID_PATTERN = settings.COURSE_ID_PATTERN
urlpatterns = patterns('', # nopep8 urlpatterns = patterns('', # nopep8
# Json request data for metrics for entire course # Json request data for metrics for entire course
url(r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/all_sequential_open_distrib$', url(r'^{}/all_sequential_open_distrib$'.format(settings.COURSE_ID_PATTERN),
'class_dashboard.views.all_sequential_open_distrib', name="all_sequential_open_distrib"), 'class_dashboard.views.all_sequential_open_distrib', name="all_sequential_open_distrib"),
url(r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/all_problem_grade_distribution$', url(r'^{}/all_problem_grade_distribution$'.format(settings.COURSE_ID_PATTERN),
'class_dashboard.views.all_problem_grade_distribution', name="all_problem_grade_distribution"), 'class_dashboard.views.all_problem_grade_distribution', name="all_problem_grade_distribution"),
# Json request data for metrics for particular section # Json request data for metrics for particular section
url(r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/problem_grade_distribution/(?P<section>\d+)$', url(r'^{}/problem_grade_distribution/(?P<section>\d+)$'.format(settings.COURSE_ID_PATTERN),
'class_dashboard.views.section_problem_grade_distrib', name="section_problem_grade_distrib"), 'class_dashboard.views.section_problem_grade_distrib', name="section_problem_grade_distrib"),
# For listing students that opened a sub-section # For listing students that opened a sub-section
......
...@@ -1033,7 +1033,7 @@ def instructor_dashboard(request, course_id): ...@@ -1033,7 +1033,7 @@ def instructor_dashboard(request, course_id):
'course_errors': modulestore().get_course_errors(course.id), 'course_errors': modulestore().get_course_errors(course.id),
'instructor_tasks': instructor_tasks, 'instructor_tasks': instructor_tasks,
'offline_grade_log': offline_grades_available(course_key), 'offline_grade_log': offline_grades_available(course_key),
'cohorts_ajax_url': reverse('cohorts', kwargs={'course_key': course_key.to_deprecated_string()}), 'cohorts_ajax_url': reverse('cohorts', kwargs={'course_key_string': course_key.to_deprecated_string()}),
'analytics_results': analytics_results, 'analytics_results': analytics_results,
'disable_buttons': disable_buttons, 'disable_buttons': disable_buttons,
......
...@@ -13,7 +13,7 @@ if settings.FEATURES['ENABLE_SHOPPING_CART']: ...@@ -13,7 +13,7 @@ if settings.FEATURES['ENABLE_SHOPPING_CART']:
url(r'^$', 'show_cart'), url(r'^$', 'show_cart'),
url(r'^clear/$', 'clear_cart'), url(r'^clear/$', 'clear_cart'),
url(r'^remove_item/$', 'remove_item'), url(r'^remove_item/$', 'remove_item'),
url(r'^add/course/(?P<course_id>[^/]+/[^/]+/[^/]+)/$', 'add_course_to_cart', name='add_course_to_cart'), url(r'^add/course/{}/$'.format(settings.COURSE_ID_PATTERN), 'add_course_to_cart', name='add_course_to_cart'),
) )
if settings.FEATURES.get('ENABLE_PAYMENT_FAKE'): if settings.FEATURES.get('ENABLE_PAYMENT_FAKE'):
......
...@@ -2,22 +2,28 @@ from django.conf.urls import patterns, url ...@@ -2,22 +2,28 @@ from django.conf.urls import patterns, url
from verify_student import views from verify_student import views
from django.conf import settings
urlpatterns = patterns( urlpatterns = patterns(
'', '',
url( url(
r'^show_requirements/(?P<course_id>[^/]+/[^/]+/[^/]+)/$', r'^show_requirements/{}/$'.format(settings.COURSE_ID_PATTERN),
views.show_requirements, views.show_requirements,
name="verify_student_show_requirements" name="verify_student_show_requirements"
), ),
# pylint sometimes seems to dislike the as_view() function because as_view() is
# decorated with `classonlymethod` instead of `classmethod`. It's inconsistent
# about *which* as_view() calls it grumbles about, but we disable those warnings
url( url(
r'^verify/(?P<course_id>[^/]+/[^/]+/[^/]+)/$', r'^verify/{}/$'.format(settings.COURSE_ID_PATTERN),
views.VerifyView.as_view(), # pylint: disable=E1120 views.VerifyView.as_view(), # pylint: disable=no-value-for-parameter
name="verify_student_verify" name="verify_student_verify"
), ),
url( url(
r'^verified/(?P<course_id>[^/]+/[^/]+/[^/]+)/$', r'^verified/{}/$'.format(settings.COURSE_ID_PATTERN),
views.VerifiedView.as_view(), views.VerifiedView.as_view(),
name="verify_student_verified" name="verify_student_verified"
), ),
...@@ -41,8 +47,8 @@ urlpatterns = patterns( ...@@ -41,8 +47,8 @@ urlpatterns = patterns(
), ),
url( url(
r'^midcourse_reverify/(?P<course_id>[^/]+/[^/]+/[^/]+)/$', r'^midcourse_reverify/{}/$'.format(settings.COURSE_ID_PATTERN),
views.MidCourseReverifyView.as_view(), # pylint: disable=E1120 views.MidCourseReverifyView.as_view(), # pylint: disable=no-value-for-parameter
name="verify_student_midcourse_reverify" name="verify_student_midcourse_reverify"
), ),
......
...@@ -399,6 +399,14 @@ COURSE_SETTINGS = { ...@@ -399,6 +399,14 @@ COURSE_SETTINGS = {
# TODO (vshnayder): Will probably need to change as we get real access control in. # TODO (vshnayder): Will probably need to change as we get real access control in.
LMS_MIGRATION_ALLOWED_IPS = [] LMS_MIGRATION_ALLOWED_IPS = []
# These are standard regexes for pulling out info like course_ids, usage_ids, etc.
# They are used so that URLs with deprecated-format strings still work.
COURSE_ID_PATTERN = r'(?P<course_id>(?:[^/]+/[^/]+/[^/]+)|(?:[^/]+))'
COURSE_KEY_PATTERN = r'(?P<course_key_string>(?:[^/]+/[^/]+/[^/]+)|(?:[^/]+))'
USAGE_KEY_PATTERN = r'(?P<usage_key_string>(?:i4x://?[^/]+/[^/]+/[^/]+/[^@]+(?:@[^/]+)?)|(?:[^/]+))'
ASSET_KEY_PATTERN = r'(?P<asset_key_string>(?:/?c4x(:/)?/[^/]+/[^/]+/[^/]+/[^@]+(?:@[^/]+)?)|(?:[^/]+))'
USAGE_ID_PATTERN = r'(?P<usage_id>(?:i4x://?[^/]+/[^/]+/[^/]+/[^@]+(?:@[^/]+)?)|(?:[^/]+))'
############################## EVENT TRACKING ################################# ############################## EVENT TRACKING #################################
......
...@@ -179,22 +179,21 @@ if settings.WIKI_ENABLED: ...@@ -179,22 +179,21 @@ if settings.WIKI_ENABLED:
# These urls are for viewing the wiki in the context of a course. They should # These urls are for viewing the wiki in the context of a course. They should
# never be returned by a reverse() so they come after the other url patterns # never be returned by a reverse() so they come after the other url patterns
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/course_wiki/?$', url(r'^courses/{}/course_wiki/?$'.format(settings.COURSE_ID_PATTERN),
'course_wiki.views.course_wiki_redirect', name="course_wiki"), 'course_wiki.views.course_wiki_redirect', name="course_wiki"),
url(r'^courses/(?:[^/]+/[^/]+/[^/]+)/wiki/', include(wiki_pattern())), url(r'^courses/(?:[^/]+/[^/]+/[^/]+)/wiki/', include(wiki_pattern())),
) )
if settings.COURSEWARE_ENABLED: if settings.COURSEWARE_ENABLED:
urlpatterns += ( urlpatterns += (
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/jump_to/(?P<location>.*)$', url(r'^courses/{}/jump_to/(?P<location>.*)$'.format(settings.COURSE_ID_PATTERN),
'courseware.views.jump_to', name="jump_to"), 'courseware.views.jump_to', name="jump_to"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/jump_to_id/(?P<module_id>.*)$', url(r'^courses/{}/jump_to_id/(?P<module_id>.*)$'.format(settings.COURSE_ID_PATTERN),
'courseware.views.jump_to_id', name="jump_to_id"), 'courseware.views.jump_to_id', name="jump_to_id"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/xblock/(?P<usage_id>[^/]*)/handler/(?P<handler>[^/]*)(?:/(?P<suffix>.*))?$', url(r'^courses/{course_key}/xblock/{usage_key}/handler/(?P<handler>[^/]*)(?:/(?P<suffix>.*))?$'.format(course_key=settings.COURSE_ID_PATTERN, usage_key=settings.USAGE_ID_PATTERN),
'courseware.module_render.handle_xblock_callback', 'courseware.module_render.handle_xblock_callback',
name='xblock_handler'), name='xblock_handler'),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/xblock/(?P<usage_id>[^/]*)/handler_noauth/(?P<handler>[^/]*)(?:/(?P<suffix>.*))?$', url(r'^courses/{course_key}/xblock/{usage_key}/handler_noauth/(?P<handler>[^/]*)(?:/(?P<suffix>.*))?$'.format(course_key=settings.COURSE_ID_PATTERN, usage_key=settings.USAGE_ID_PATTERN),
'courseware.module_render.handle_xblock_callback_noauth', 'courseware.module_render.handle_xblock_callback_noauth',
name='xblock_handler_noauth'), name='xblock_handler_noauth'),
url(r'xblock/resource/(?P<block_type>[^/]+)/(?P<uri>.*)$', url(r'xblock/resource/(?P<block_type>[^/]+)/(?P<uri>.*)$',
...@@ -209,7 +208,7 @@ if settings.COURSEWARE_ENABLED: ...@@ -209,7 +208,7 @@ if settings.COURSEWARE_ENABLED:
# into the database. # into the database.
url(r'^software-licenses$', 'licenses.views.user_software_license', name="user_software_license"), url(r'^software-licenses$', 'licenses.views.user_software_license', name="user_software_license"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/xqueue/(?P<userid>[^/]*)/(?P<mod_id>.*?)/(?P<dispatch>[^/]*)$', url(r'^courses/{}/xqueue/(?P<userid>[^/]*)/(?P<mod_id>.*?)/(?P<dispatch>[^/]*)$'.format(settings.COURSE_ID_PATTERN),
'courseware.module_render.xqueue_callback', 'courseware.module_render.xqueue_callback',
name='xqueue_callback'), name='xqueue_callback'),
url(r'^change_setting$', 'student.views.change_setting', url(r'^change_setting$', 'student.views.change_setting',
...@@ -227,117 +226,117 @@ if settings.COURSEWARE_ENABLED: ...@@ -227,117 +226,117 @@ if settings.COURSEWARE_ENABLED:
url(r'^change_email_settings$', 'student.views.change_email_settings', name="change_email_settings"), url(r'^change_email_settings$', 'student.views.change_email_settings', name="change_email_settings"),
#About the course #About the course
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/about$', url(r'^courses/{}/about$'.format(settings.COURSE_ID_PATTERN),
'courseware.views.course_about', name="about_course"), 'courseware.views.course_about', name="about_course"),
#View for mktg site (kept for backwards compatibility TODO - remove before merge to master) #View for mktg site (kept for backwards compatibility TODO - remove before merge to master)
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/mktg-about$', url(r'^courses/{}/mktg-about$'.format(settings.COURSE_ID_PATTERN),
'courseware.views.mktg_course_about', name="mktg_about_course"), 'courseware.views.mktg_course_about', name="mktg_about_course"),
#View for mktg site #View for mktg site
url(r'^mktg/(?P<course_id>[^/]+/[^/]+/[^/]+)/?$', url(r'^mktg/{}/?$'.format(settings.COURSE_ID_PATTERN),
'courseware.views.mktg_course_about', name="mktg_about_course"), 'courseware.views.mktg_course_about', name="mktg_about_course"),
#Inside the course #Inside the course
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/$', url(r'^courses/{}/$'.format(settings.COURSE_ID_PATTERN),
'courseware.views.course_info', name="course_root"), 'courseware.views.course_info', name="course_root"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/info$', url(r'^courses/{}/info$'.format(settings.COURSE_ID_PATTERN),
'courseware.views.course_info', name="info"), 'courseware.views.course_info', name="info"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/syllabus$', url(r'^courses/{}/syllabus$'.format(settings.COURSE_ID_PATTERN),
'courseware.views.syllabus', name="syllabus"), # TODO arjun remove when custom tabs in place, see courseware/courses.py 'courseware.views.syllabus', name="syllabus"), # TODO arjun remove when custom tabs in place, see courseware/courses.py
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/book/(?P<book_index>\d+)/$', url(r'^courses/{}/book/(?P<book_index>\d+)/$'.format(settings.COURSE_ID_PATTERN),
'staticbook.views.index', name="book"), 'staticbook.views.index', name="book"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/book/(?P<book_index>\d+)/(?P<page>\d+)$', url(r'^courses/{}/book/(?P<book_index>\d+)/(?P<page>\d+)$'.format(settings.COURSE_ID_PATTERN),
'staticbook.views.index', name="book"), 'staticbook.views.index', name="book"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/pdfbook/(?P<book_index>\d+)/$', url(r'^courses/{}/pdfbook/(?P<book_index>\d+)/$'.format(settings.COURSE_ID_PATTERN),
'staticbook.views.pdf_index', name="pdf_book"), 'staticbook.views.pdf_index', name="pdf_book"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/pdfbook/(?P<book_index>\d+)/(?P<page>\d+)$', url(r'^courses/{}/pdfbook/(?P<book_index>\d+)/(?P<page>\d+)$'.format(settings.COURSE_ID_PATTERN),
'staticbook.views.pdf_index', name="pdf_book"), 'staticbook.views.pdf_index', name="pdf_book"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/pdfbook/(?P<book_index>\d+)/chapter/(?P<chapter>\d+)/$', url(r'^courses/{}/pdfbook/(?P<book_index>\d+)/chapter/(?P<chapter>\d+)/$'.format(settings.COURSE_ID_PATTERN),
'staticbook.views.pdf_index', name="pdf_book"), 'staticbook.views.pdf_index', name="pdf_book"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/pdfbook/(?P<book_index>\d+)/chapter/(?P<chapter>\d+)/(?P<page>\d+)$', url(r'^courses/{}/pdfbook/(?P<book_index>\d+)/chapter/(?P<chapter>\d+)/(?P<page>\d+)$'.format(settings.COURSE_ID_PATTERN),
'staticbook.views.pdf_index', name="pdf_book"), 'staticbook.views.pdf_index', name="pdf_book"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/htmlbook/(?P<book_index>\d+)/$', url(r'^courses/{}/htmlbook/(?P<book_index>\d+)/$'.format(settings.COURSE_ID_PATTERN),
'staticbook.views.html_index', name="html_book"), 'staticbook.views.html_index', name="html_book"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/htmlbook/(?P<book_index>\d+)/chapter/(?P<chapter>\d+)/$', url(r'^courses/{}/htmlbook/(?P<book_index>\d+)/chapter/(?P<chapter>\d+)/$'.format(settings.COURSE_ID_PATTERN),
'staticbook.views.html_index', name="html_book"), 'staticbook.views.html_index', name="html_book"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/courseware/?$', url(r'^courses/{}/courseware/?$'.format(settings.COURSE_ID_PATTERN),
'courseware.views.index', name="courseware"), 'courseware.views.index', name="courseware"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/courseware/(?P<chapter>[^/]*)/$', url(r'^courses/{}/courseware/(?P<chapter>[^/]*)/$'.format(settings.COURSE_ID_PATTERN),
'courseware.views.index', name="courseware_chapter"), 'courseware.views.index', name="courseware_chapter"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/courseware/(?P<chapter>[^/]*)/(?P<section>[^/]*)/$', url(r'^courses/{}/courseware/(?P<chapter>[^/]*)/(?P<section>[^/]*)/$'.format(settings.COURSE_ID_PATTERN),
'courseware.views.index', name="courseware_section"), 'courseware.views.index', name="courseware_section"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/courseware/(?P<chapter>[^/]*)/(?P<section>[^/]*)/(?P<position>[^/]*)/?$', url(r'^courses/{}/courseware/(?P<chapter>[^/]*)/(?P<section>[^/]*)/(?P<position>[^/]*)/?$'.format(settings.COURSE_ID_PATTERN),
'courseware.views.index', name="courseware_position"), 'courseware.views.index', name="courseware_position"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/progress$', url(r'^courses/{}/progress$'.format(settings.COURSE_ID_PATTERN),
'courseware.views.progress', name="progress"), 'courseware.views.progress', name="progress"),
# Takes optional student_id for instructor use--shows profile as that student sees it. # Takes optional student_id for instructor use--shows profile as that student sees it.
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/progress/(?P<student_id>[^/]*)/$', url(r'^courses/{}/progress/(?P<student_id>[^/]*)/$'.format(settings.COURSE_ID_PATTERN),
'courseware.views.progress', name="student_progress"), 'courseware.views.progress', name="student_progress"),
# For the instructor # For the instructor
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/instructor$', url(r'^courses/{}/instructor$'.format(settings.COURSE_ID_PATTERN),
'instructor.views.instructor_dashboard.instructor_dashboard_2', name="instructor_dashboard"), 'instructor.views.instructor_dashboard.instructor_dashboard_2', name="instructor_dashboard"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/instructor/api/', url(r'^courses/{}/instructor/api/'.format(settings.COURSE_ID_PATTERN),
include('instructor.views.api_urls')), include('instructor.views.api_urls')),
# see ENABLE_INSTRUCTOR_LEGACY_DASHBOARD section for legacy dash urls # see ENABLE_INSTRUCTOR_LEGACY_DASHBOARD section for legacy dash urls
# Open Ended grading views # Open Ended grading views
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/staff_grading$', url(r'^courses/{}/staff_grading$'.format(settings.COURSE_ID_PATTERN),
'open_ended_grading.views.staff_grading', name='staff_grading'), 'open_ended_grading.views.staff_grading', name='staff_grading'),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/staff_grading/get_next$', url(r'^courses/{}/staff_grading/get_next$'.format(settings.COURSE_ID_PATTERN),
'open_ended_grading.staff_grading_service.get_next', name='staff_grading_get_next'), 'open_ended_grading.staff_grading_service.get_next', name='staff_grading_get_next'),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/staff_grading/save_grade$', url(r'^courses/{}/staff_grading/save_grade$'.format(settings.COURSE_ID_PATTERN),
'open_ended_grading.staff_grading_service.save_grade', name='staff_grading_save_grade'), 'open_ended_grading.staff_grading_service.save_grade', name='staff_grading_save_grade'),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/staff_grading/get_problem_list$', url(r'^courses/{}/staff_grading/get_problem_list$'.format(settings.COURSE_ID_PATTERN),
'open_ended_grading.staff_grading_service.get_problem_list', name='staff_grading_get_problem_list'), 'open_ended_grading.staff_grading_service.get_problem_list', name='staff_grading_get_problem_list'),
# Open Ended problem list # Open Ended problem list
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/open_ended_problems$', url(r'^courses/{}/open_ended_problems$'.format(settings.COURSE_ID_PATTERN),
'open_ended_grading.views.student_problem_list', name='open_ended_problems'), 'open_ended_grading.views.student_problem_list', name='open_ended_problems'),
# Open Ended flagged problem list # Open Ended flagged problem list
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/open_ended_flagged_problems$', url(r'^courses/{}/open_ended_flagged_problems$'.format(settings.COURSE_ID_PATTERN),
'open_ended_grading.views.flagged_problem_list', name='open_ended_flagged_problems'), 'open_ended_grading.views.flagged_problem_list', name='open_ended_flagged_problems'),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/open_ended_flagged_problems/take_action_on_flags$', url(r'^courses/{}/open_ended_flagged_problems/take_action_on_flags$'.format(settings.COURSE_ID_PATTERN),
'open_ended_grading.views.take_action_on_flags', name='open_ended_flagged_problems_take_action'), 'open_ended_grading.views.take_action_on_flags', name='open_ended_flagged_problems_take_action'),
# Cohorts management # Cohorts management
url(r'^courses/(?P<course_key>[^/]+/[^/]+/[^/]+)/cohorts$', url(r'^courses/{}/cohorts$'.format(settings.COURSE_KEY_PATTERN),
'course_groups.views.list_cohorts', name="cohorts"), 'course_groups.views.list_cohorts', name="cohorts"),
url(r'^courses/(?P<course_key>[^/]+/[^/]+/[^/]+)/cohorts/add$', url(r'^courses/{}/cohorts/add$'.format(settings.COURSE_KEY_PATTERN),
'course_groups.views.add_cohort', 'course_groups.views.add_cohort',
name="add_cohort"), name="add_cohort"),
url(r'^courses/(?P<course_key>[^/]+/[^/]+/[^/]+)/cohorts/(?P<cohort_id>[0-9]+)$', url(r'^courses/{}/cohorts/(?P<cohort_id>[0-9]+)$'.format(settings.COURSE_KEY_PATTERN),
'course_groups.views.users_in_cohort', 'course_groups.views.users_in_cohort',
name="list_cohort"), name="list_cohort"),
url(r'^courses/(?P<course_key>[^/]+/[^/]+/[^/]+)/cohorts/(?P<cohort_id>[0-9]+)/add$', url(r'^courses/{}/cohorts/(?P<cohort_id>[0-9]+)/add$'.format(settings.COURSE_KEY_PATTERN),
'course_groups.views.add_users_to_cohort', 'course_groups.views.add_users_to_cohort',
name="add_to_cohort"), name="add_to_cohort"),
url(r'^courses/(?P<course_key>[^/]+/[^/]+/[^/]+)/cohorts/(?P<cohort_id>[0-9]+)/delete$', url(r'^courses/{}/cohorts/(?P<cohort_id>[0-9]+)/delete$'.format(settings.COURSE_KEY_PATTERN),
'course_groups.views.remove_user_from_cohort', 'course_groups.views.remove_user_from_cohort',
name="remove_from_cohort"), name="remove_from_cohort"),
url(r'^courses/(?P<course_key>[^/]+/[^/]+/[^/]+)/cohorts/debug$', url(r'^courses/{}/cohorts/debug$'.format(settings.COURSE_KEY_PATTERN),
'course_groups.views.debug_cohort_mgmt', 'course_groups.views.debug_cohort_mgmt',
name="debug_cohort_mgmt"), name="debug_cohort_mgmt"),
# Open Ended Notifications # Open Ended Notifications
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/open_ended_notifications$', url(r'^courses/{}/open_ended_notifications$'.format(settings.COURSE_ID_PATTERN),
'open_ended_grading.views.combined_notifications', name='open_ended_notifications'), 'open_ended_grading.views.combined_notifications', name='open_ended_notifications'),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/peer_grading$', url(r'^courses/{}/peer_grading$'.format(settings.COURSE_ID_PATTERN),
'open_ended_grading.views.peer_grading', name='peer_grading'), 'open_ended_grading.views.peer_grading', name='peer_grading'),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/notes$', 'notes.views.notes', name='notes'), url(r'^courses/{}/notes$'.format(settings.COURSE_ID_PATTERN), 'notes.views.notes', name='notes'),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/notes/', include('notes.urls')), url(r'^courses/{}/notes/'.format(settings.COURSE_ID_PATTERN), include('notes.urls')),
# LTI endpoints listing # LTI endpoints listing
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/lti_rest_endpoints/', url(r'^courses/{}/lti_rest_endpoints/'.format(settings.COURSE_ID_PATTERN),
'courseware.views.get_course_lti_endpoints', name='lti_rest_endpoints'), 'courseware.views.get_course_lti_endpoints', name='lti_rest_endpoints'),
) )
...@@ -350,7 +349,7 @@ if settings.COURSEWARE_ENABLED: ...@@ -350,7 +349,7 @@ if settings.COURSEWARE_ENABLED:
# discussion forums live within courseware, so courseware must be enabled first # discussion forums live within courseware, so courseware must be enabled first
if settings.FEATURES.get('ENABLE_DISCUSSION_SERVICE'): if settings.FEATURES.get('ENABLE_DISCUSSION_SERVICE'):
urlpatterns += ( urlpatterns += (
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/discussion/', url(r'^courses/{}/discussion/'.format(settings.COURSE_ID_PATTERN),
include('django_comment_client.urls')), include('django_comment_client.urls')),
url(r'^notification_prefs/enable/', 'notification_prefs.views.ajax_enable'), url(r'^notification_prefs/enable/', 'notification_prefs.views.ajax_enable'),
url(r'^notification_prefs/disable/', 'notification_prefs.views.ajax_disable'), url(r'^notification_prefs/disable/', 'notification_prefs.views.ajax_disable'),
...@@ -362,13 +361,13 @@ if settings.COURSEWARE_ENABLED: ...@@ -362,13 +361,13 @@ if settings.COURSEWARE_ENABLED:
) )
urlpatterns += ( urlpatterns += (
# This MUST be the last view in the courseware--it's a catch-all for custom tabs. # This MUST be the last view in the courseware--it's a catch-all for custom tabs.
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/(?P<tab_slug>[^/]+)/$', url(r'^courses/{}/(?P<tab_slug>[^/]+)/$'.format(settings.COURSE_ID_PATTERN),
'courseware.views.static_tab', name="static_tab"), 'courseware.views.static_tab', name="static_tab"),
) )
if settings.FEATURES.get('ENABLE_STUDENT_HISTORY_VIEW'): if settings.FEATURES.get('ENABLE_STUDENT_HISTORY_VIEW'):
urlpatterns += ( urlpatterns += (
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/submission_history/(?P<student_username>[^/]*)/(?P<location>.*?)$', url(r'^courses/{}/submission_history/(?P<student_username>[^/]*)/(?P<location>.*?)$'.format(settings.COURSE_ID_PATTERN),
'courseware.views.submission_history', 'courseware.views.submission_history',
name='submission_history'), name='submission_history'),
) )
...@@ -376,9 +375,9 @@ if settings.COURSEWARE_ENABLED: ...@@ -376,9 +375,9 @@ if settings.COURSEWARE_ENABLED:
if settings.COURSEWARE_ENABLED and settings.FEATURES.get('ENABLE_INSTRUCTOR_LEGACY_DASHBOARD'): if settings.COURSEWARE_ENABLED and settings.FEATURES.get('ENABLE_INSTRUCTOR_LEGACY_DASHBOARD'):
urlpatterns += ( urlpatterns += (
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/legacy_grade_summary$', url(r'^courses/{}/legacy_grade_summary$'.format(settings.COURSE_ID_PATTERN),
'instructor.views.legacy.grade_summary', name='grade_summary_legacy'), 'instructor.views.legacy.grade_summary', name='grade_summary_legacy'),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/legacy_instructor_dash$', url(r'^courses/{}/legacy_instructor_dash$'.format(settings.COURSE_ID_PATTERN),
'instructor.views.legacy.instructor_dashboard', name="instructor_dashboard_legacy"), 'instructor.views.legacy.instructor_dashboard', name="instructor_dashboard_legacy"),
) )
...@@ -411,9 +410,9 @@ if settings.FEATURES.get('AUTH_USE_CAS'): ...@@ -411,9 +410,9 @@ if settings.FEATURES.get('AUTH_USE_CAS'):
if settings.FEATURES.get('RESTRICT_ENROLL_BY_REG_METHOD'): if settings.FEATURES.get('RESTRICT_ENROLL_BY_REG_METHOD'):
urlpatterns += ( urlpatterns += (
url(r'^course_specific_login/(?P<course_id>[^/]+/[^/]+/[^/]+)/$', url(r'^course_specific_login/{}/$'.format(settings.COURSE_ID_PATTERN),
'external_auth.views.course_specific_login', name='course-specific-login'), 'external_auth.views.course_specific_login', name='course-specific-login'),
url(r'^course_specific_register/(?P<course_id>[^/]+/[^/]+/[^/]+)/$', url(r'^course_specific_register/{}/$'.format(settings.COURSE_ID_PATTERN),
'external_auth.views.course_specific_register', name='course-specific-register'), 'external_auth.views.course_specific_register', name='course-specific-register'),
) )
...@@ -477,7 +476,7 @@ if settings.FEATURES.get('ENABLE_DEBUG_RUN_PYTHON'): ...@@ -477,7 +476,7 @@ if settings.FEATURES.get('ENABLE_DEBUG_RUN_PYTHON'):
# Crowdsourced hinting instructor manager. # Crowdsourced hinting instructor manager.
if settings.FEATURES.get('ENABLE_HINTER_INSTRUCTOR_VIEW'): if settings.FEATURES.get('ENABLE_HINTER_INSTRUCTOR_VIEW'):
urlpatterns += ( urlpatterns += (
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/hint_manager$', url(r'^courses/{}/hint_manager$'.format(settings.COURSE_ID_PATTERN),
'instructor.hint_manager.hint_manager', name="hint_manager"), 'instructor.hint_manager.hint_manager', name="hint_manager"),
) )
......
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