urls.py 32.8 KB
Newer Older
1 2 3 4
"""
URLs for LMS
"""

Piotr Mitros committed
5
from django.conf import settings
6
from django.conf.urls import patterns, include, url
7
from django.views.generic.base import RedirectView
8
from ratelimitbackend import admin
9
from django.conf.urls.static import static
10

11
from courseware.views.views import CourseTabView, EnrollStaffView, StaticCourseTabView
12
from config_models.views import ConfigurationModelCurrentAPIView
13
from courseware.views.index import CoursewareIndex
14
from django_comment_common.models import ForumsConfig
15
from openedx.core.djangoapps.auth_exchange.views import LoginWithAccessTokenView
16
from openedx.core.djangoapps.catalog.models import CatalogIntegration
17
from openedx.core.djangoapps.programs.models import ProgramsApiConfig
18
from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
19
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
20
from openedx.features.enterprise_support.api import enterprise_enabled
21

Piotr Mitros committed
22
# Uncomment the next two lines to enable the admin:
23
if settings.DEBUG or settings.FEATURES.get('ENABLE_DJANGO_ADMIN_SITE'):
24
    admin.autodiscover()
Piotr Mitros committed
25

26
# Use urlpatterns formatted as within the Django docs with first parameter "stuck" to the open parenthesis
stv committed
27 28 29
urlpatterns = (
    '',

Calen Pennington committed
30
    url(r'^$', 'branding.views.index', name="root"),   # Main marketing page, or redirect to courseware
31 32 33

    url(r'', include('student.urls')),
    # TODO: Move lms specific student views out of common code
34
    url(r'^dashboard$', 'student.views.dashboard', name="dashboard"),
35
    url(r'^change_enrollment$', 'student.views.change_enrollment', name='change_enrollment'),
36

37 38 39
    # Event tracking endpoints
    url(r'', include('track.urls')),

40 41
    # Performance endpoints
    url(r'', include('openedx.core.djangoapps.performance.urls')),
42

43 44
    # Static template view endpoints like blog, faq, etc.
    url(r'', include('static_template_view.urls')),
45

46
    url(r'^heartbeat$', include('openedx.core.djangoapps.heartbeat.urls')),
47

Andy Armstrong committed
48
    # Note: these are older versions of the User API that will eventually be
49
    # subsumed by api/user listed below.
Andy Armstrong committed
50
    url(r'^user_api/', include('openedx.core.djangoapps.user_api.legacy_urls')),
51

52 53
    url(r'^notifier_api/', include('notifier_api.urls')),

54
    url(r'^i18n/', include('django.conf.urls.i18n')),
55

56 57
    # Feedback Form endpoint
    url(r'^submit_feedback$', 'util.views.submit_feedback'),
58

59
    # Enrollment API RESTful endpoints
60
    url(r'^api/enrollment/v1/', include('enrollment.urls')),
61

62 63 64
    # Courseware search endpoints
    url(r'^search/', include('search.urls')),

65 66
    # Course content API
    url(r'^api/course_structure/', include('course_structure_api.urls', namespace='course_structure_api')),
67

Nimisha Asthagiri committed
68 69 70
    # Course API
    url(r'^api/courses/', include('course_api.urls')),

71 72 73
    # User API endpoints
    url(r'^api/user/', include('openedx.core.djangoapps.user_api.urls')),

muzaffaryousaf committed
74
    # Bookmarks API endpoints
75
    url(r'^api/bookmarks/', include('openedx.core.djangoapps.bookmarks.urls')),
muzaffaryousaf committed
76

77 78
    # Profile Images API endpoints
    url(r'^api/profile_images/', include('openedx.core.djangoapps.profile_images.urls')),
79 80 81 82

    # Video Abstraction Layer used to allow video teams to manage video assets
    # independently of courseware. https://github.com/edx/edx-val
    url(r'^api/val/v0/', include('edxval.urls')),
83 84

    url(r'^api/commerce/', include('commerce.api.urls', namespace='commerce_api')),
Clinton Blackburn committed
85
    url(r'^api/credit/', include('openedx.core.djangoapps.credit.urls', app_name="credit", namespace='credit')),
86
    url(r'^rss_proxy/', include('rss_proxy.urls', namespace='rss_proxy')),
87
    url(r'^api/organizations/', include('organizations.urls', namespace='organizations')),
88

89 90
    url(r'^catalog/', include('openedx.core.djangoapps.catalog.urls', namespace='catalog')),

asadiqbal committed
91
    # Update session view
92 93 94 95 96
    url(
        r'^lang_pref/session_language',
        'openedx.core.djangoapps.lang_pref.views.update_session_language',
        name='session_language'
    ),
asadiqbal committed
97

98 99 100 101
    # Multiple course modes and identity verification
    # TODO Namespace these!
    url(r'^course_modes/', include('course_modes.urls')),
    url(r'^verify_student/', include('verify_student.urls')),
Bill DeRusha committed
102

103 104 105
    # URLs for managing dark launches of languages
    url(r'^update_lang/', include('openedx.core.djangoapps.dark_lang.urls', namespace='dark_lang')),

106 107 108
    # For redirecting to help pages.
    url(r'^help_token/', include('help_tokens.urls')),

Bill DeRusha committed
109 110
    # URLs for API access management
    url(r'^api-admin/', include('openedx.core.djangoapps.api_admin.urls', namespace='api_admin')),
111
)
112

113 114 115 116
urlpatterns += (
    url(r'^dashboard/', include('learner_dashboard.urls')),
)

117 118
# TODO: This needs to move to a separate urls.py once the student_account and
# student views below find a home together
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
if settings.FEATURES["ENABLE_COMBINED_LOGIN_REGISTRATION"]:
    # Backwards compatibility with old URL structure, but serve the new views
    urlpatterns += (
        url(r'^login$', 'student_account.views.login_and_registration_form',
            {'initial_mode': 'login'}, name="signin_user"),
        url(r'^register$', 'student_account.views.login_and_registration_form',
            {'initial_mode': 'register'}, name="register_user"),
    )
else:
    # Serve the old views
    urlpatterns += (
        url(r'^login$', 'student.views.signin_user', name="signin_user"),
        url(r'^register$', 'student.views.register_user', name="register_user"),
    )

134 135 136 137 138
if settings.FEATURES["ENABLE_MOBILE_REST_API"]:
    urlpatterns += (
        url(r'^api/mobile/v0.5/', include('mobile_api.urls')),
    )

139 140
if settings.FEATURES["ENABLE_OPENBADGES"]:
    urlpatterns += (
141
        url(r'^api/badges/v1/', include('badges.api.urls', app_name="badges", namespace="badges_api")),
142 143
    )

144 145
js_info_dict = {
    'domain': 'djangojs',
146 147
    # We need to explicitly include external Django apps that are not in LOCALE_PATHS.
    'packages': ('openassessment',),
148 149
}

Carson Gee committed
150 151 152 153 154 155
# sysadmin dashboard, to see what courses are loaded, to delete & load courses
if settings.FEATURES["ENABLE_SYSADMIN_DASHBOARD"]:
    urlpatterns += (
        url(r'^sysadmin/', include('dashboard.sysadmin_urls')),
    )

156
urlpatterns += (
157
    url(r'^support/', include('support.urls', app_name="support", namespace='support')),
158 159
)

160
# Favicon
161
favicon_path = configuration_helpers.get_value('favicon_path', settings.FAVICON_PATH)  # pylint: disable=invalid-name
162
urlpatterns += (url(
163
    r'^favicon\.ico$',
164
    RedirectView.as_view(url=settings.STATIC_URL + favicon_path, permanent=True)
165 166
),)

167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
# Multicourse wiki (Note: wiki urls must be above the courseware ones because of
# the custom tab catch-all)
if settings.WIKI_ENABLED:
    from wiki.urls import get_pattern as wiki_pattern
    from django_notify.urls import get_pattern as notify_pattern

    urlpatterns += (
        # First we include views from course_wiki that we use to override the default views.
        # They come first in the urlpatterns so they get resolved first
        url('^wiki/create-root/$', 'course_wiki.views.root_create', name='root_create'),
        url(r'^wiki/', include(wiki_pattern())),
        url(r'^notify/', include(notify_pattern())),

        # 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
182
        url(r'^courses/{}/course_wiki/?$'.format(settings.COURSE_ID_PATTERN),
183
            'course_wiki.views.course_wiki_redirect', name="course_wiki"),
184
        url(r'^courses/{}/wiki/'.format(settings.COURSE_KEY_REGEX), include(wiki_pattern())),
185 186
    )

stv committed
187 188 189 190
COURSE_URLS = patterns(
    '',
    url(
        r'^look_up_registration_code$',
191
        'lms.djangoapps.instructor.views.registration_codes.look_up_registration_code',
stv committed
192
        name='look_up_registration_code',
stv committed
193 194 195
    ),
    url(
        r'^registration_code_details$',
196
        'lms.djangoapps.instructor.views.registration_codes.registration_code_details',
stv committed
197 198
        name='registration_code_details',
    ),
stv committed
199 200 201 202
)
urlpatterns += (
    # jump_to URLs for direct access to a location in the course
    url(
stv committed
203 204 205
        r'^courses/{}/jump_to/(?P<location>.*)$'.format(
            settings.COURSE_ID_PATTERN,
        ),
206
        'courseware.views.views.jump_to',
stv committed
207
        name='jump_to',
stv committed
208 209
    ),
    url(
stv committed
210 211 212
        r'^courses/{}/jump_to_id/(?P<module_id>.*)$'.format(
            settings.COURSE_ID_PATTERN,
        ),
213
        'courseware.views.views.jump_to_id',
stv committed
214
        name='jump_to_id',
stv committed
215 216 217 218 219 220 221
    ),

    # xblock Handler APIs
    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,
222
        ),
stv committed
223 224 225 226 227 228 229
        'courseware.module_render.handle_xblock_callback',
        name='xblock_handler',
    ),
    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,
230
        ),
stv committed
231 232 233 234 235 236 237 238 239 240 241
        'courseware.module_render.handle_xblock_callback_noauth',
        name='xblock_handler_noauth',
    ),

    # xblock View API
    # (unpublished) API that returns JSON with the HTML fragment and related resources
    # for the xBlock's requested view.
    url(
        r'^courses/{course_key}/xblock/{usage_key}/view/(?P<view_name>[^/]*)$'.format(
            course_key=settings.COURSE_ID_PATTERN,
            usage_key=settings.USAGE_ID_PATTERN,
242
        ),
stv committed
243 244 245 246 247 248 249 250 251 252
        'courseware.module_render.xblock_view',
        name='xblock_view',
    ),

    # xblock Rendering View URL
    # URL to provide an HTML view of an xBlock. The view type (e.g., student_view) is
    # passed as a "view" parameter to the URL.
    # Note: This is not an API. Compare this with the xblock_view API above.
    url(
        r'^xblock/{usage_key_string}$'.format(usage_key_string=settings.USAGE_KEY_PATTERN),
253
        'courseware.views.views.render_xblock',
stv committed
254 255 256 257 258 259 260 261 262 263 264 265
        name='render_xblock',
    ),

    # xblock Resource URL
    url(
        r'xblock/resource/(?P<block_type>[^/]+)/(?P<uri>.*)$',
        'openedx.core.djangoapps.common_views.xblock.xblock_resource',
        name='xblock_resource_url',
    ),

    url(
        r'^courses/{}/xqueue/(?P<userid>[^/]*)/(?P<mod_id>.*?)/(?P<dispatch>[^/]*)$'.format(
stv committed
266
            settings.COURSE_ID_PATTERN,
267
        ),
stv committed
268
        'courseware.module_render.xqueue_callback',
stv committed
269 270
        name='xqueue_callback',
    ),
stv committed
271 272 273 274 275 276 277

    # TODO: These views need to be updated before they work
    url(r'^calculate$', 'util.views.calculate'),

    url(r'^courses/?$', 'branding.views.courses', name="courses"),

    #About the course
stv committed
278 279 280 281
    url(
        r'^courses/{}/about$'.format(
            settings.COURSE_ID_PATTERN,
        ),
282
        'courseware.views.views.course_about',
stv committed
283 284
        name='about_course',
    ),
stv committed
285

Awais Jibran committed
286 287 288 289
    url(
        r'^courses/{}/enroll_staff$'.format(
            settings.COURSE_ID_PATTERN,
        ),
290
        EnrollStaffView.as_view(),
Awais Jibran committed
291 292 293
        name='enroll_staff',
    ),

stv committed
294
    #Inside the course
stv committed
295 296 297 298
    url(
        r'^courses/{}/$'.format(
            settings.COURSE_ID_PATTERN,
        ),
299
        'courseware.views.views.course_info',
stv committed
300 301 302 303 304 305
        name='course_root',
    ),
    url(
        r'^courses/{}/info$'.format(
            settings.COURSE_ID_PATTERN,
        ),
306
        'courseware.views.views.course_info',
stv committed
307 308
        name='info',
    ),
stv committed
309
    # TODO arjun remove when custom tabs in place, see courseware/courses.py
stv committed
310 311 312 313
    url(
        r'^courses/{}/syllabus$'.format(
            settings.COURSE_ID_PATTERN,
        ),
314
        'courseware.views.views.syllabus',
stv committed
315 316
        name='syllabus',
    ),
stv committed
317

stv committed
318 319 320 321 322
    # Survey associated with a course
    url(
        r'^courses/{}/survey$'.format(
            settings.COURSE_ID_PATTERN,
        ),
323
        'courseware.views.views.course_survey',
stv committed
324 325
        name='course_survey',
    ),
stv committed
326

stv committed
327 328 329 330 331 332 333 334 335 336 337 338 339 340
    url(
        r'^courses/{}/book/(?P<book_index>\d+)/$'.format(
            settings.COURSE_ID_PATTERN,
        ),
        'staticbook.views.index',
        name='book',
    ),
    url(
        r'^courses/{}/book/(?P<book_index>\d+)/(?P<page>\d+)$'.format(
            settings.COURSE_ID_PATTERN,
        ),
        'staticbook.views.index',
        name='book',
    ),
stv committed
341

stv committed
342 343 344 345 346 347 348 349 350 351 352 353 354 355
    url(
        r'^courses/{}/pdfbook/(?P<book_index>\d+)/$'.format(
            settings.COURSE_ID_PATTERN,
        ),
        'staticbook.views.pdf_index',
        name='pdf_book',
    ),
    url(
        r'^courses/{}/pdfbook/(?P<book_index>\d+)/(?P<page>\d+)$'.format(
            settings.COURSE_ID_PATTERN,
        ),
        'staticbook.views.pdf_index',
        name='pdf_book',
    ),
stv committed
356

stv committed
357 358 359 360 361 362 363
    url(
        r'^courses/{}/pdfbook/(?P<book_index>\d+)/chapter/(?P<chapter>\d+)/$'.format(
            settings.COURSE_ID_PATTERN,
        ),
        'staticbook.views.pdf_index',
        name='pdf_book',
    ),
stv committed
364 365
    url(
        r'^courses/{}/pdfbook/(?P<book_index>\d+)/chapter/(?P<chapter>\d+)/(?P<page>\d+)$'.format(
stv committed
366
            settings.COURSE_ID_PATTERN,
367
        ),
stv committed
368 369
        'staticbook.views.pdf_index',
        name='pdf_book',
stv committed
370 371
    ),

stv committed
372 373 374 375 376 377 378 379 380 381 382 383 384 385
    url(
        r'^courses/{}/htmlbook/(?P<book_index>\d+)/$'.format(
            settings.COURSE_ID_PATTERN,
        ),
        'staticbook.views.html_index',
        name='html_book',
    ),
    url(
        r'^courses/{}/htmlbook/(?P<book_index>\d+)/chapter/(?P<chapter>\d+)/$'.format(
            settings.COURSE_ID_PATTERN,
        ),
        'staticbook.views.html_index',
        name='html_book',
    ),
stv committed
386

stv committed
387 388 389 390
    url(
        r'^courses/{}/courseware/?$'.format(
            settings.COURSE_ID_PATTERN,
        ),
391
        CoursewareIndex.as_view(),
stv committed
392 393 394 395 396 397
        name='courseware',
    ),
    url(
        r'^courses/{}/courseware/(?P<chapter>[^/]*)/$'.format(
            settings.COURSE_ID_PATTERN,
        ),
398
        CoursewareIndex.as_view(),
stv committed
399 400 401 402 403 404
        name='courseware_chapter',
    ),
    url(
        r'^courses/{}/courseware/(?P<chapter>[^/]*)/(?P<section>[^/]*)/$'.format(
            settings.COURSE_ID_PATTERN,
        ),
405
        CoursewareIndex.as_view(),
stv committed
406 407
        name='courseware_section',
    ),
stv committed
408 409
    url(
        r'^courses/{}/courseware/(?P<chapter>[^/]*)/(?P<section>[^/]*)/(?P<position>[^/]*)/?$'.format(
stv committed
410
            settings.COURSE_ID_PATTERN,
411
        ),
412
        CoursewareIndex.as_view(),
stv committed
413
        name='courseware_position',
stv committed
414 415
    ),

416
    # progress page
stv committed
417 418 419 420
    url(
        r'^courses/{}/progress$'.format(
            settings.COURSE_ID_PATTERN,
        ),
421
        'courseware.views.views.progress',
stv committed
422 423
        name='progress',
    ),
424

stv committed
425
    # Takes optional student_id for instructor use--shows profile as that student sees it.
stv committed
426 427 428 429
    url(
        r'^courses/{}/progress/(?P<student_id>[^/]*)/$'.format(
            settings.COURSE_ID_PATTERN,
        ),
430
        'courseware.views.views.progress',
stv committed
431 432
        name='student_progress',
    ),
stv committed
433

Hasnain committed
434 435 436 437 438 439 440 441
    url(
        r'^programs/{}/about'.format(
            r'(?P<program_uuid>[0-9a-f-]+)',
        ),
        'courseware.views.views.program_marketing',
        name='program_marketing_view',
    ),

442 443 444 445 446 447 448
    # rest api for grades
    url(
        r'^api/grades/',
        include('lms.djangoapps.grades.api.urls', namespace='grades_api')
    ),


stv committed
449
    # For the instructor
stv committed
450 451 452 453
    url(
        r'^courses/{}/instructor$'.format(
            settings.COURSE_ID_PATTERN,
        ),
454
        'lms.djangoapps.instructor.views.instructor_dashboard.instructor_dashboard_2',
stv committed
455 456
        name='instructor_dashboard',
    ),
stv committed
457 458


stv committed
459 460 461 462
    url(
        r'^courses/{}/set_course_mode_price$'.format(
            settings.COURSE_ID_PATTERN,
        ),
463
        'lms.djangoapps.instructor.views.instructor_dashboard.set_course_mode_price',
stv committed
464 465 466 467 468 469
        name='set_course_mode_price',
    ),
    url(
        r'^courses/{}/instructor/api/'.format(
            settings.COURSE_ID_PATTERN,
        ),
470
        include('lms.djangoapps.instructor.views.api_urls')),
stv committed
471 472 473 474
    url(
        r'^courses/{}/remove_coupon$'.format(
            settings.COURSE_ID_PATTERN,
        ),
475
        'lms.djangoapps.instructor.views.coupons.remove_coupon',
stv committed
476 477 478 479 480 481
        name='remove_coupon',
    ),
    url(
        r'^courses/{}/add_coupon$'.format(
            settings.COURSE_ID_PATTERN,
        ),
482
        'lms.djangoapps.instructor.views.coupons.add_coupon',
stv committed
483 484 485 486 487 488
        name='add_coupon',
    ),
    url(
        r'^courses/{}/update_coupon$'.format(
            settings.COURSE_ID_PATTERN,
        ),
489
        'lms.djangoapps.instructor.views.coupons.update_coupon',
stv committed
490 491 492 493 494 495
        name='update_coupon',
    ),
    url(
        r'^courses/{}/get_coupon_info$'.format(
            settings.COURSE_ID_PATTERN,
        ),
496
        'lms.djangoapps.instructor.views.coupons.get_coupon_info',
stv committed
497 498 499 500 501 502 503 504 505
        name='get_coupon_info',
    ),

    url(
        r'^courses/{}/'.format(
            settings.COURSE_ID_PATTERN,
        ),
        include(COURSE_URLS)
    ),
stv committed
506

507 508 509 510 511 512 513 514 515
    # Discussions Management
    url(
        r'^courses/{}/discussions/settings$'.format(
            settings.COURSE_KEY_PATTERN,
        ),
        'openedx.core.djangoapps.course_groups.views.course_discussions_settings_handler',
        name='course_discussions_settings',
    ),

stv committed
516
    # Cohorts management
stv committed
517 518 519 520
    url(
        r'^courses/{}/cohorts/settings$'.format(
            settings.COURSE_KEY_PATTERN,
        ),
stv committed
521
        'openedx.core.djangoapps.course_groups.views.course_cohort_settings_handler',
stv committed
522 523 524 525 526 527 528 529 530 531 532 533 534
        name='course_cohort_settings',
    ),
    url(
        r'^courses/{}/cohorts/(?P<cohort_id>[0-9]+)?$'.format(
            settings.COURSE_KEY_PATTERN,
        ),
        'openedx.core.djangoapps.course_groups.views.cohort_handler',
        name='cohorts',
    ),
    url(
        r'^courses/{}/cohorts/(?P<cohort_id>[0-9]+)$'.format(
            settings.COURSE_KEY_PATTERN,
        ),
stv committed
535
        'openedx.core.djangoapps.course_groups.views.users_in_cohort',
stv committed
536 537 538 539 540 541
        name='list_cohort',
    ),
    url(
        r'^courses/{}/cohorts/(?P<cohort_id>[0-9]+)/add$'.format(
            settings.COURSE_KEY_PATTERN,
        ),
stv committed
542
        'openedx.core.djangoapps.course_groups.views.add_users_to_cohort',
stv committed
543 544 545 546 547 548
        name='add_to_cohort',
    ),
    url(
        r'^courses/{}/cohorts/(?P<cohort_id>[0-9]+)/delete$'.format(
            settings.COURSE_KEY_PATTERN,
        ),
stv committed
549
        'openedx.core.djangoapps.course_groups.views.remove_user_from_cohort',
stv committed
550 551 552 553 554 555
        name='remove_from_cohort',
    ),
    url(
        r'^courses/{}/cohorts/debug$'.format(
            settings.COURSE_KEY_PATTERN,
        ),
stv committed
556
        'openedx.core.djangoapps.course_groups.views.debug_cohort_mgmt',
stv committed
557 558 559
        name='debug_cohort_mgmt',
    ),
    url(
560
        r'^courses/{}/discussion/topics$'.format(
stv committed
561 562
            settings.COURSE_KEY_PATTERN,
        ),
563 564
        'openedx.core.djangoapps.course_groups.views.discussion_topics',
        name='discussion_topics',
stv committed
565
    ),
566 567 568 569
    url(
        r'^courses/{}/verified_track_content/settings'.format(
            settings.COURSE_KEY_PATTERN,
        ),
570
        'openedx.core.djangoapps.verified_track_content.views.cohorting_settings',
571 572
        name='verified_track_cohorting',
    ),
stv committed
573 574 575 576 577 578 579 580 581 582 583 584 585
    url(
        r'^courses/{}/notes$'.format(
            settings.COURSE_ID_PATTERN,
        ),
        'notes.views.notes',
        name='notes',
    ),
    url(
        r'^courses/{}/notes/'.format(
            settings.COURSE_ID_PATTERN,
        ),
        include('notes.urls')
    ),
stv committed
586 587

    # LTI endpoints listing
stv committed
588 589 590 591
    url(
        r'^courses/{}/lti_rest_endpoints/'.format(
            settings.COURSE_ID_PATTERN,
        ),
592
        'courseware.views.views.get_course_lti_endpoints',
stv committed
593 594
        name='lti_rest_endpoints',
    ),
stv committed
595 596

    # Student account
stv committed
597 598 599 600
    url(
        r'^account/',
        include('student_account.urls')
    ),
stv committed
601 602

    # Student profile
stv committed
603 604 605 606 607
    url(
        r'^u/(?P<username>[\w.@+-]+)$',
        'student_profile.views.learner_profile',
        name='learner_profile',
    ),
stv committed
608 609

    # Student Notes
stv committed
610 611 612 613 614 615 616
    url(
        r'^courses/{}/edxnotes'.format(
            settings.COURSE_ID_PATTERN,
        ),
        include('edxnotes.urls'),
        name='edxnotes_endpoints',
    ),
stv committed
617

618
    # Branding API
stv committed
619 620 621 622
    url(
        r'^api/branding/v1/',
        include('branding.api_urls')
    ),
623 624 625 626 627 628 629 630

    # Course experience
    url(
        r'^courses/{}/course/'.format(
            settings.COURSE_ID_PATTERN,
        ),
        include('openedx.features.course_experience.urls'),
    ),
631 632 633 634 635 636 637 638

    # Course bookmarks
    url(
        r'^courses/{}/bookmarks/'.format(
            settings.COURSE_ID_PATTERN,
        ),
        include('openedx.features.course_bookmarks.urls'),
    ),
stv committed
639
)
Brian Wilson committed
640

stv committed
641 642 643
if settings.FEATURES["ENABLE_TEAMS"]:
    # Teams endpoints
    urlpatterns += (
stv committed
644 645 646 647 648 649 650 651 652 653 654
        url(
            r'^api/team/',
            include('lms.djangoapps.teams.api_urls')
        ),
        url(
            r'^courses/{}/teams'.format(
                settings.COURSE_ID_PATTERN,
            ),
            include('lms.djangoapps.teams.urls'),
            name='teams_endpoints',
        ),
stv committed
655
    )
Brian Wilson committed
656

stv committed
657 658 659
# allow course staff to change to student view of courseware
if settings.FEATURES.get('ENABLE_MASQUERADE'):
    urlpatterns += (
stv committed
660 661 662 663 664 665 666
        url(
            r'^courses/{}/masquerade$'.format(
                settings.COURSE_KEY_PATTERN,
            ),
            'courseware.masquerade.handle_ajax',
            name='masquerade_update',
        ),
667
    )
668

stv committed
669
urlpatterns += (
stv committed
670 671 672 673
    url(
        r'^courses/{}/generate_user_cert'.format(
            settings.COURSE_ID_PATTERN,
        ),
674
        'courseware.views.views.generate_user_cert',
stv committed
675 676
        name='generate_user_cert',
    ),
stv committed
677
)
678

stv committed
679 680
# discussion forums live within courseware, so courseware must be enabled first
if settings.FEATURES.get('ENABLE_DISCUSSION_SERVICE'):
681
    urlpatterns += (
stv committed
682 683 684 685 686 687 688 689 690 691 692
        url(
            r'^api/discussion/',
            include('discussion_api.urls')
        ),
        url(
            r'^courses/{}/discussion/'.format(
                settings.COURSE_ID_PATTERN,
            ),
            include('django_comment_client.urls')
        ),
        url(
693 694 695 696 697 698
            r'^courses/{}/discussion/forum/'.format(
                settings.COURSE_ID_PATTERN,
            ),
            include('discussion.urls')
        ),
        url(
stv committed
699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725
            r'^notification_prefs/enable/',
            'notification_prefs.views.ajax_enable'
        ),
        url(
            r'^notification_prefs/disable/',
            'notification_prefs.views.ajax_disable'
        ),
        url(
            r'^notification_prefs/status/',
            'notification_prefs.views.ajax_status'
        ),
        url(
            r'^notification_prefs/unsubscribe/(?P<token>[a-zA-Z0-9-_=]+)/',
            'notification_prefs.views.set_subscription',
            {
                'subscribe': False,
            },
            name='unsubscribe_forum_update',
        ),
        url(
            r'^notification_prefs/resubscribe/(?P<token>[a-zA-Z0-9-_=]+)/',
            'notification_prefs.views.set_subscription',
            {
                'subscribe': True,
            },
            name='resubscribe_forum_update',
        ),
726
    )
727 728 729 730 731 732

urlpatterns += (
    url(
        r'^courses/{}/tab/(?P<tab_type>[^/]+)/$'.format(
            settings.COURSE_ID_PATTERN,
        ),
733 734
        CourseTabView.as_view(),
        name='course_tab_view',
735 736 737
    ),
)

stv committed
738 739
urlpatterns += (
    # This MUST be the last view in the courseware--it's a catch-all for custom tabs.
stv committed
740 741 742 743
    url(
        r'^courses/{}/(?P<tab_slug>[^/]+)/$'.format(
            settings.COURSE_ID_PATTERN,
        ),
744
        StaticCourseTabView.as_view(),
stv committed
745 746
        name='static_tab',
    ),
stv committed
747
)
748

stv committed
749
if settings.FEATURES.get('ENABLE_STUDENT_HISTORY_VIEW'):
750
    urlpatterns += (
stv committed
751 752 753 754
        url(
            r'^courses/{}/submission_history/(?P<student_username>[^/]*)/(?P<location>.*?)$'.format(
                settings.COURSE_ID_PATTERN
            ),
755
            'courseware.views.views.submission_history',
stv committed
756 757
            name='submission_history',
        ),
David Baumgold committed
758
    )
759

760 761
if settings.FEATURES.get('CLASS_DASHBOARD'):
    urlpatterns += (
762
        url(r'^class_dashboard/', include('class_dashboard.urls')),
763 764
    )

765
if settings.DEBUG or settings.FEATURES.get('ENABLE_DJANGO_ADMIN_SITE'):
766
    ## Jasmine and admin
767
    urlpatterns += (url(r'^admin/', include(admin.site.urls)),)
768

769
if settings.FEATURES.get('AUTH_USE_OPENID'):
ichuang committed
770 771
    urlpatterns += (
        url(r'^openid/login/$', 'django_openid_auth.views.login_begin', name='openid-login'),
772 773 774 775 776
        url(
            r'^openid/complete/$',
            'openedx.core.djangoapps.external_auth.views.openid_login_complete',
            name='openid-complete',
        ),
ichuang committed
777
        url(r'^openid/logo.gif$', 'django_openid_auth.views.logo', name='openid-logo'),
778 779
    )

780
if settings.FEATURES.get('AUTH_USE_SHIB'):
781
    urlpatterns += (
782
        url(r'^shib-login/$', 'openedx.core.djangoapps.external_auth.views.shib_login', name='shib-login'),
783 784
    )

785
if settings.FEATURES.get('AUTH_USE_CAS'):
786
    urlpatterns += (
787
        url(r'^cas-auth/login/$', 'openedx.core.djangoapps.external_auth.views.cas_login', name="cas-login"),
788 789 790
        url(r'^cas-auth/logout/$', 'django_cas.views.logout', {'next_page': '/'}, name="cas-logout"),
    )

791
if settings.FEATURES.get('RESTRICT_ENROLL_BY_REG_METHOD'):
792
    urlpatterns += (
793
        url(r'^course_specific_login/{}/$'.format(settings.COURSE_ID_PATTERN),
794
            'openedx.core.djangoapps.external_auth.views.course_specific_login', name='course-specific-login'),
795
        url(r'^course_specific_register/{}/$'.format(settings.COURSE_ID_PATTERN),
796
            'openedx.core.djangoapps.external_auth.views.course_specific_register', name='course-specific-register'),
797 798 799

    )

800 801
# Shopping cart
urlpatterns += (
802
    url(r'^shoppingcart/', include('shoppingcart.urls')),
803
    url(r'^commerce/', include('commerce.urls', namespace='commerce')),
804 805
)

806 807
# Embargo
if settings.FEATURES.get('EMBARGO'):
808
    urlpatterns += (
809
        url(r'^embargo/', include('openedx.core.djangoapps.embargo.urls')),
810 811
    )

812 813 814 815
# Survey Djangoapp
urlpatterns += (
    url(r'^survey/', include('survey.urls')),
)
816

817
if settings.FEATURES.get('AUTH_USE_OPENID_PROVIDER'):
818
    urlpatterns += (
819 820 821 822 823
        url(
            r'^openid/provider/login/$',
            'openedx.core.djangoapps.external_auth.views.provider_login',
            name='openid-provider-login',
        ),
824 825
        url(
            r'^openid/provider/login/(?:.+)$',
826
            'openedx.core.djangoapps.external_auth.views.provider_identity',
827 828
            name='openid-provider-login-identity'
        ),
829 830 831 832 833 834 835 836 837 838
        url(
            r'^openid/provider/identity/$',
            'openedx.core.djangoapps.external_auth.views.provider_identity',
            name='openid-provider-identity',
        ),
        url(
            r'^openid/provider/xrds/$',
            'openedx.core.djangoapps.external_auth.views.provider_xrds',
            name='openid-provider-xrds',
        ),
839
    )
ichuang committed
840

841 842
if settings.FEATURES.get('ENABLE_OAUTH2_PROVIDER'):
    urlpatterns += (
843 844
        # These URLs dispatch to django-oauth-toolkit or django-oauth2-provider as appropriate.
        # Developers should use these routes, to maintain compatibility for existing client code
845
        url(r'^oauth2/', include('openedx.core.djangoapps.oauth_dispatch.urls')),
846 847 848
        # These URLs contain the django-oauth2-provider default behavior.  It exists to provide
        # URLs for django-oauth2-provider to call using reverse() with the oauth2 namespace, and
        # also to maintain support for views that have not yet been wrapped in dispatch views.
849
        url(r'^oauth2/', include('edx_oauth2_provider.urls', namespace='oauth2')),
850 851 852 853
        # The /_o/ prefix exists to provide a target for code in django-oauth-toolkit that
        # uses reverse() with the 'oauth2_provider' namespace.  Developers should not access these
        # views directly, but should rather use the wrapped views at /oauth2/
        url(r'^_o/', include('oauth2_provider.urls', namespace='oauth2_provider')),
854 855
    )

856
if settings.FEATURES.get('ENABLE_LMS_MIGRATION'):
857 858 859
    urlpatterns += (
        url(r'^migrate/modules$', 'lms_migration.migrate.manage_modulestores'),
        url(r'^migrate/reload/(?P<reload_dir>[^/]+)$', 'lms_migration.migrate.manage_modulestores'),
860 861 862 863
        url(
            r'^migrate/reload/(?P<reload_dir>[^/]+)/(?P<commit_id>[^/]+)$',
            'lms_migration.migrate.manage_modulestores'
        ),
864 865
        url(r'^gitreload$', 'lms_migration.migrate.gitreload'),
        url(r'^gitreload/(?P<reload_dir>[^/]+)$', 'lms_migration.migrate.gitreload'),
David Baumgold committed
866
    )
867

868
if settings.FEATURES.get('ENABLE_SQL_TRACKING_LOGS'):
869 870
    urlpatterns += (
        url(r'^event_logs$', 'track.views.view_tracking_log'),
871
        url(r'^event_logs/(?P<args>.+)$', 'track.views.view_tracking_log'),
David Baumgold committed
872
    )
873

874
if settings.FEATURES.get('ENABLE_SERVICE_STATUS'):
875
    urlpatterns += (
876
        url(r'^status/', include('openedx.core.djangoapps.service_status.urls')),
877 878
    )

879
if settings.FEATURES.get('ENABLE_INSTRUCTOR_BACKGROUND_TASKS'):
880
    urlpatterns += (
881 882
        url(
            r'^instructor_task_status/$',
883
            'lms.djangoapps.instructor_task.views.instructor_task_status',
884 885
            name='instructor_task_status'
        ),
886 887
    )

888
if settings.FEATURES.get('RUN_AS_ANALYTICS_SERVER_ENABLED'):
889
    urlpatterns += (
Juho Kim committed
890
        url(r'^edinsights_service/', include('edinsights.core.urls')),
891 892
    )

893
if settings.FEATURES.get('ENABLE_DEBUG_RUN_PYTHON'):
894
    urlpatterns += (
895
        url(r'^debug/run_python$', 'debug.views.run_python'),
896
    )
897

898
urlpatterns += (
899
    url(r'^debug/show_parameters$', 'debug.views.show_parameters'),
900 901
)

902

903 904 905 906
# Third-party auth.
if settings.FEATURES.get('ENABLE_THIRD_PARTY_AUTH'):
    urlpatterns += (
        url(r'', include('third_party_auth.urls')),
907
        url(r'api/third_party_auth/', include('third_party_auth.api.urls')),
908 909 910 911 912
        # NOTE: The following login_oauth_token endpoint is DEPRECATED.
        # Please use the exchange_access_token endpoint instead.
        url(r'^login_oauth_token/(?P<backend>[^/]+)/$', 'student.views.login_oauth_token'),
    )

913 914 915 916 917 918
# Enterprise
if enterprise_enabled():
    urlpatterns += (
        url(r'', include('enterprise.urls')),
    )

919
# OAuth token exchange
920
if settings.FEATURES.get('ENABLE_OAUTH2_PROVIDER'):
921
    urlpatterns += (
922
        url(
923
            r'^oauth2/login/$',
924
            LoginWithAccessTokenView.as_view(),
925
            name="login_with_access_token"
926
        ),
927 928
    )

929
# Certificates
930
urlpatterns += (
931
    url(r'^certificates/', include('certificates.urls', app_name="certificates", namespace="certificates")),
932

933 934 935 936
    # Backwards compatibility with XQueue, which uses URLs that are not prefixed with /certificates/
    url(r'^update_certificate$', 'certificates.views.update_certificate'),
    url(r'^update_example_certificate$', 'certificates.views.update_example_certificate'),
    url(r'^request_certificate$', 'certificates.views.request_certificate'),
937 938 939 940

    # REST APIs
    url(r'^api/certificates/',
        include('lms.djangoapps.certificates.apis.urls', namespace='certificates_api')),
941 942
)

943 944
# XDomain proxy
urlpatterns += (
945
    url(r'^xdomain_proxy.html$', 'openedx.core.djangoapps.cors_csrf.views.xdomain_proxy', name='xdomain_proxy'),
946 947
)

948 949 950 951 952
# Custom courses on edX (CCX) URLs
if settings.FEATURES["CUSTOM_COURSES_EDX"]:
    urlpatterns += (
        url(r'^courses/{}/'.format(settings.COURSE_ID_PATTERN),
            include('ccx.urls')),
953
        url(r'^api/ccx/', include('lms.djangoapps.ccx.api.urls', namespace='ccx_api')),
954 955
    )

956 957 958 959 960
# Access to courseware as an LTI provider
if settings.FEATURES.get("ENABLE_LTI_PROVIDER"):
    urlpatterns += (
        url(r'^lti_provider/', include('lti_provider.urls')),
    )
961

962 963
urlpatterns += (
    url(r'config/self_paced', ConfigurationModelCurrentAPIView.as_view(model=SelfPacedConfiguration)),
964
    url(r'config/programs', ConfigurationModelCurrentAPIView.as_view(model=ProgramsApiConfig)),
965
    url(r'config/catalog', ConfigurationModelCurrentAPIView.as_view(model=CatalogIntegration)),
966
    url(r'config/forums', ConfigurationModelCurrentAPIView.as_view(model=ForumsConfig)),
967 968
)

969 970
urlpatterns = patterns(*urlpatterns)

971
if settings.DEBUG:
972
    urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
973
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
974
    urlpatterns += static(
975 976
        settings.PROFILE_IMAGE_BACKEND['options']['base_url'],
        document_root=settings.PROFILE_IMAGE_BACKEND['options']['location']
977
    )
978

979
urlpatterns += url(r'^template/(?P<template>.+)$', 'openedx.core.djangoapps.debug.views.show_reference_template'),
980

981
if 'debug_toolbar' in settings.INSTALLED_APPS:
982 983 984 985 986
    import debug_toolbar
    urlpatterns += (
        url(r'^__debug__/', include(debug_toolbar.urls)),
    )

987 988 989 990 991 992 993

# Custom error pages
# These are used by Django to render these error codes. Do not remove.
# pylint: disable=invalid-name
handler404 = 'static_template_view.views.render_404'
handler500 = 'static_template_view.views.render_500'

994 995 996 997
# include into our URL patterns the HTTP REST API that comes with edx-proctoring.
urlpatterns += (
    url(r'^api/', include('edx_proctoring.urls')),
)
998 999 1000 1001 1002

if settings.FEATURES.get('ENABLE_FINANCIAL_ASSISTANCE_FORM'):
    urlpatterns += (
        url(
            r'^financial-assistance/$',
1003
            'courseware.views.views.financial_assistance',
1004 1005 1006 1007
            name='financial_assistance'
        ),
        url(
            r'^financial-assistance/apply/$',
1008
            'courseware.views.views.financial_assistance_form',
1009 1010 1011 1012
            name='financial_assistance_form'
        ),
        url(
            r'^financial-assistance/submit/$',
1013
            'courseware.views.views.financial_assistance_request',
1014 1015 1016
            name='submit_financial_assistance_request'
        )
    )