Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
38fba248
Commit
38fba248
authored
Nov 26, 2013
by
Don Mitchell
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1754 from edx/dhm/restful_settings
Restful api for course advanced settings
parents
389df173
7f91ce40
Show whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
207 additions
and
189 deletions
+207
-189
cms/djangoapps/contentstore/tests/test_contentstore.py
+1
-9
cms/djangoapps/contentstore/tests/test_course_settings.py
+79
-11
cms/djangoapps/contentstore/views/course.py
+85
-93
cms/djangoapps/models/settings/course_details.py
+5
-5
cms/djangoapps/models/settings/course_grading.py
+6
-6
cms/djangoapps/models/settings/course_metadata.py
+17
-44
cms/templates/settings.html
+2
-3
cms/templates/settings_advanced.html
+5
-5
cms/templates/settings_graders.html
+4
-4
cms/templates/widgets/header.html
+2
-1
cms/urls.py
+1
-7
common/djangoapps/util/json_request.py
+0
-1
No files found.
cms/djangoapps/contentstore/tests/test_contentstore.py
View file @
38fba248
...
@@ -1660,15 +1660,7 @@ class ContentStoreTest(ModuleStoreTestCase):
...
@@ -1660,15 +1660,7 @@ class ContentStoreTest(ModuleStoreTestCase):
test_get_html
(
'tabs'
)
test_get_html
(
'tabs'
)
test_get_html
(
'settings/details'
)
test_get_html
(
'settings/details'
)
test_get_html
(
'settings/grading'
)
test_get_html
(
'settings/grading'
)
test_get_html
(
'settings/advanced'
)
# advanced settings
resp
=
self
.
client
.
get_html
(
reverse
(
'course_advanced_settings'
,
kwargs
=
{
'org'
:
loc
.
org
,
'course'
:
loc
.
course
,
'name'
:
loc
.
name
}))
self
.
assertEqual
(
resp
.
status_code
,
200
)
# TODO: uncomment when advanced settings not using old locations.
# _test_no_locations(self, resp)
# textbook index
# textbook index
resp
=
self
.
client
.
get_html
(
reverse
(
'textbook_index'
,
resp
=
self
.
client
.
get_html
(
reverse
(
'textbook_index'
,
...
...
cms/djangoapps/contentstore/tests/test_course_settings.py
View file @
38fba248
...
@@ -9,10 +9,9 @@ import mock
...
@@ -9,10 +9,9 @@ import mock
from
django.utils.timezone
import
UTC
from
django.utils.timezone
import
UTC
from
django.test.utils
import
override_settings
from
django.test.utils
import
override_settings
from
xmodule.modulestore
import
Location
from
models.settings.course_details
import
(
CourseDetails
,
CourseSettingsEncoder
)
from
models.settings.course_details
import
(
CourseDetails
,
CourseSettingsEncoder
)
from
models.settings.course_grading
import
CourseGradingModel
from
models.settings.course_grading
import
CourseGradingModel
from
contentstore.utils
import
get_modulestore
from
contentstore.utils
import
get_modulestore
,
EXTRA_TAB_PANELS
from
xmodule.modulestore.tests.factories
import
CourseFactory
from
xmodule.modulestore.tests.factories
import
CourseFactory
...
@@ -20,7 +19,8 @@ from models.settings.course_metadata import CourseMetadata
...
@@ -20,7 +19,8 @@ from models.settings.course_metadata import CourseMetadata
from
xmodule.fields
import
Date
from
xmodule.fields
import
Date
from
.utils
import
CourseTestCase
from
.utils
import
CourseTestCase
from
xmodule.modulestore.django
import
loc_mapper
from
xmodule.modulestore.django
import
loc_mapper
,
modulestore
from
contentstore.views.component
import
ADVANCED_COMPONENT_POLICY_KEY
class
CourseDetailsTestCase
(
CourseTestCase
):
class
CourseDetailsTestCase
(
CourseTestCase
):
...
@@ -418,15 +418,19 @@ class CourseMetadataEditingTest(CourseTestCase):
...
@@ -418,15 +418,19 @@ class CourseMetadataEditingTest(CourseTestCase):
"""
"""
def
setUp
(
self
):
def
setUp
(
self
):
CourseTestCase
.
setUp
(
self
)
CourseTestCase
.
setUp
(
self
)
CourseFactory
.
create
(
org
=
'edX'
,
course
=
'999'
,
display_name
=
'Robot Super Course'
)
self
.
fullcourse
=
CourseFactory
.
create
(
org
=
'edX'
,
course
=
'999'
,
display_name
=
'Robot Super Course'
)
self
.
fullcourse_location
=
Location
([
'i4x'
,
'edX'
,
'999'
,
'course'
,
'Robot_Super_Course'
,
None
])
self
.
course_setting_url
=
self
.
course_locator
.
url_reverse
(
'settings/advanced'
)
self
.
fullcourse_setting_url
=
loc_mapper
()
.
translate_location
(
self
.
fullcourse
.
location
.
course_id
,
self
.
fullcourse
.
location
,
False
,
True
)
.
url_reverse
(
'settings/advanced'
)
def
test_fetch_initial_fields
(
self
):
def
test_fetch_initial_fields
(
self
):
test_model
=
CourseMetadata
.
fetch
(
self
.
course
.
location
)
test_model
=
CourseMetadata
.
fetch
(
self
.
course
)
self
.
assertIn
(
'display_name'
,
test_model
,
'Missing editable metadata field'
)
self
.
assertIn
(
'display_name'
,
test_model
,
'Missing editable metadata field'
)
self
.
assertEqual
(
test_model
[
'display_name'
],
'Robot Super Course'
,
"not expected value"
)
self
.
assertEqual
(
test_model
[
'display_name'
],
'Robot Super Course'
,
"not expected value"
)
test_model
=
CourseMetadata
.
fetch
(
self
.
fullcourse
_location
)
test_model
=
CourseMetadata
.
fetch
(
self
.
fullcourse
)
self
.
assertNotIn
(
'graceperiod'
,
test_model
,
'blacklisted field leaked in'
)
self
.
assertNotIn
(
'graceperiod'
,
test_model
,
'blacklisted field leaked in'
)
self
.
assertIn
(
'display_name'
,
test_model
,
'full missing editable metadata field'
)
self
.
assertIn
(
'display_name'
,
test_model
,
'full missing editable metadata field'
)
self
.
assertEqual
(
test_model
[
'display_name'
],
'Robot Super Course'
,
"not expected value"
)
self
.
assertEqual
(
test_model
[
'display_name'
],
'Robot Super Course'
,
"not expected value"
)
...
@@ -435,17 +439,18 @@ class CourseMetadataEditingTest(CourseTestCase):
...
@@ -435,17 +439,18 @@ class CourseMetadataEditingTest(CourseTestCase):
self
.
assertIn
(
'xqa_key'
,
test_model
,
'xqa_key field '
)
self
.
assertIn
(
'xqa_key'
,
test_model
,
'xqa_key field '
)
def
test_update_from_json
(
self
):
def
test_update_from_json
(
self
):
test_model
=
CourseMetadata
.
update_from_json
(
self
.
course
.
location
,
{
test_model
=
CourseMetadata
.
update_from_json
(
self
.
course
,
{
"advertised_start"
:
"start A"
,
"advertised_start"
:
"start A"
,
"testcenter_info"
:
{
"c"
:
"test"
},
"testcenter_info"
:
{
"c"
:
"test"
},
"days_early_for_beta"
:
2
"days_early_for_beta"
:
2
})
})
self
.
update_check
(
test_model
)
self
.
update_check
(
test_model
)
# try fresh fetch to ensure persistence
# try fresh fetch to ensure persistence
test_model
=
CourseMetadata
.
fetch
(
self
.
course
.
location
)
fresh
=
modulestore
()
.
get_item
(
self
.
course_location
)
test_model
=
CourseMetadata
.
fetch
(
fresh
)
self
.
update_check
(
test_model
)
self
.
update_check
(
test_model
)
# now change some of the existing metadata
# now change some of the existing metadata
test_model
=
CourseMetadata
.
update_from_json
(
self
.
course
.
location
,
{
test_model
=
CourseMetadata
.
update_from_json
(
fresh
,
{
"advertised_start"
:
"start B"
,
"advertised_start"
:
"start B"
,
"display_name"
:
"jolly roger"
}
"display_name"
:
"jolly roger"
}
)
)
...
@@ -465,7 +470,11 @@ class CourseMetadataEditingTest(CourseTestCase):
...
@@ -465,7 +470,11 @@ class CourseMetadataEditingTest(CourseTestCase):
self
.
assertEqual
(
test_model
[
'days_early_for_beta'
],
2
,
"days_early_for_beta not expected value"
)
self
.
assertEqual
(
test_model
[
'days_early_for_beta'
],
2
,
"days_early_for_beta not expected value"
)
def
test_delete_key
(
self
):
def
test_delete_key
(
self
):
test_model
=
CourseMetadata
.
delete_key
(
self
.
fullcourse_location
,
{
'deleteKeys'
:
[
'doesnt_exist'
,
'showanswer'
,
'xqa_key'
]})
test_model
=
CourseMetadata
.
update_from_json
(
self
.
fullcourse
,
{
"unsetKeys"
:
[
'showanswer'
,
'xqa_key'
]
}
)
# ensure no harm
# ensure no harm
self
.
assertNotIn
(
'graceperiod'
,
test_model
,
'blacklisted field leaked in'
)
self
.
assertNotIn
(
'graceperiod'
,
test_model
,
'blacklisted field leaked in'
)
self
.
assertIn
(
'display_name'
,
test_model
,
'full missing editable metadata field'
)
self
.
assertIn
(
'display_name'
,
test_model
,
'full missing editable metadata field'
)
...
@@ -475,6 +484,65 @@ class CourseMetadataEditingTest(CourseTestCase):
...
@@ -475,6 +484,65 @@ class CourseMetadataEditingTest(CourseTestCase):
self
.
assertEqual
(
'finished'
,
test_model
[
'showanswer'
],
'showanswer field still in'
)
self
.
assertEqual
(
'finished'
,
test_model
[
'showanswer'
],
'showanswer field still in'
)
self
.
assertEqual
(
None
,
test_model
[
'xqa_key'
],
'xqa_key field still in'
)
self
.
assertEqual
(
None
,
test_model
[
'xqa_key'
],
'xqa_key field still in'
)
def
test_http_fetch_initial_fields
(
self
):
response
=
self
.
client
.
get_json
(
self
.
course_setting_url
)
test_model
=
json
.
loads
(
response
.
content
)
self
.
assertIn
(
'display_name'
,
test_model
,
'Missing editable metadata field'
)
self
.
assertEqual
(
test_model
[
'display_name'
],
'Robot Super Course'
,
"not expected value"
)
response
=
self
.
client
.
get_json
(
self
.
fullcourse_setting_url
)
test_model
=
json
.
loads
(
response
.
content
)
self
.
assertNotIn
(
'graceperiod'
,
test_model
,
'blacklisted field leaked in'
)
self
.
assertIn
(
'display_name'
,
test_model
,
'full missing editable metadata field'
)
self
.
assertEqual
(
test_model
[
'display_name'
],
'Robot Super Course'
,
"not expected value"
)
self
.
assertIn
(
'rerandomize'
,
test_model
,
'Missing rerandomize metadata field'
)
self
.
assertIn
(
'showanswer'
,
test_model
,
'showanswer field '
)
self
.
assertIn
(
'xqa_key'
,
test_model
,
'xqa_key field '
)
def
test_http_update_from_json
(
self
):
response
=
self
.
client
.
ajax_post
(
self
.
course_setting_url
,
{
"advertised_start"
:
"start A"
,
"testcenter_info"
:
{
"c"
:
"test"
},
"days_early_for_beta"
:
2
,
"unsetKeys"
:
[
'showanswer'
,
'xqa_key'
],
})
test_model
=
json
.
loads
(
response
.
content
)
self
.
update_check
(
test_model
)
self
.
assertEqual
(
'finished'
,
test_model
[
'showanswer'
],
'showanswer field still in'
)
self
.
assertEqual
(
None
,
test_model
[
'xqa_key'
],
'xqa_key field still in'
)
response
=
self
.
client
.
get_json
(
self
.
course_setting_url
)
test_model
=
json
.
loads
(
response
.
content
)
self
.
update_check
(
test_model
)
# now change some of the existing metadata
response
=
self
.
client
.
ajax_post
(
self
.
course_setting_url
,
{
"advertised_start"
:
"start B"
,
"display_name"
:
"jolly roger"
})
test_model
=
json
.
loads
(
response
.
content
)
self
.
assertIn
(
'display_name'
,
test_model
,
'Missing editable metadata field'
)
self
.
assertEqual
(
test_model
[
'display_name'
],
'jolly roger'
,
"not expected value"
)
self
.
assertIn
(
'advertised_start'
,
test_model
,
'Missing revised advertised_start metadata field'
)
self
.
assertEqual
(
test_model
[
'advertised_start'
],
'start B'
,
"advertised_start not expected value"
)
def
test_advanced_components_munge_tabs
(
self
):
"""
Test that adding and removing specific advanced components adds and removes tabs.
"""
self
.
assertNotIn
(
EXTRA_TAB_PANELS
.
get
(
"open_ended"
),
self
.
course
.
tabs
)
self
.
assertNotIn
(
EXTRA_TAB_PANELS
.
get
(
"notes"
),
self
.
course
.
tabs
)
self
.
client
.
ajax_post
(
self
.
course_setting_url
,
{
ADVANCED_COMPONENT_POLICY_KEY
:
[
"combinedopenended"
]
})
course
=
modulestore
()
.
get_item
(
self
.
course_location
)
self
.
assertIn
(
EXTRA_TAB_PANELS
.
get
(
"open_ended"
),
course
.
tabs
)
self
.
assertNotIn
(
EXTRA_TAB_PANELS
.
get
(
"notes"
),
course
.
tabs
)
self
.
client
.
ajax_post
(
self
.
course_setting_url
,
{
ADVANCED_COMPONENT_POLICY_KEY
:
[]
})
course
=
modulestore
()
.
get_item
(
self
.
course_location
)
self
.
assertNotIn
(
EXTRA_TAB_PANELS
.
get
(
"open_ended"
),
course
.
tabs
)
class
CourseGraderUpdatesTest
(
CourseTestCase
):
class
CourseGraderUpdatesTest
(
CourseTestCase
):
"""
"""
...
...
cms/djangoapps/contentstore/views/course.py
View file @
38fba248
...
@@ -27,8 +27,7 @@ from xmodule.modulestore.exceptions import (
...
@@ -27,8 +27,7 @@ from xmodule.modulestore.exceptions import (
ItemNotFoundError
,
InvalidLocationError
)
ItemNotFoundError
,
InvalidLocationError
)
from
xmodule.modulestore
import
Location
from
xmodule.modulestore
import
Location
from
contentstore.course_info_model
import
(
from
contentstore.course_info_model
import
get_course_updates
,
update_course_updates
,
delete_course_update
get_course_updates
,
update_course_updates
,
delete_course_update
)
from
contentstore.utils
import
(
from
contentstore.utils
import
(
get_lms_link_for_item
,
add_extra_panel_tab
,
remove_extra_panel_tab
,
get_lms_link_for_item
,
add_extra_panel_tab
,
remove_extra_panel_tab
,
get_modulestore
)
get_modulestore
)
...
@@ -57,8 +56,8 @@ from contentstore import utils
...
@@ -57,8 +56,8 @@ from contentstore import utils
__all__
=
[
'course_info_handler'
,
'course_handler'
,
'course_info_update_handler'
,
__all__
=
[
'course_info_handler'
,
'course_handler'
,
'course_info_update_handler'
,
'settings_handler'
,
'settings_handler'
,
'grading_handler'
,
'grading_handler'
,
'
course_config_advanced_page
'
,
'
advanced_settings_handler
'
,
'
course_advanced_updates'
,
'
textbook_index'
,
'textbook_by_id'
,
'textbook_index'
,
'textbook_by_id'
,
'create_textbook'
]
'create_textbook'
]
...
@@ -175,7 +174,6 @@ def course_index(request, course_id, branch, version_guid, block):
...
@@ -175,7 +174,6 @@ def course_index(request, course_id, branch, version_guid, block):
if
not
has_access
(
request
.
user
,
location
):
if
not
has_access
(
request
.
user
,
location
):
raise
PermissionDenied
()
raise
PermissionDenied
()
old_location
=
loc_mapper
()
.
translate_locator_to_location
(
location
)
old_location
=
loc_mapper
()
.
translate_locator_to_location
(
location
)
lms_link
=
get_lms_link_for_item
(
old_location
)
lms_link
=
get_lms_link_for_item
(
old_location
)
...
@@ -228,14 +226,20 @@ def create_new_course(request):
...
@@ -228,14 +226,20 @@ def create_new_course(request):
pass
pass
if
existing_course
is
not
None
:
if
existing_course
is
not
None
:
return
JsonResponse
({
return
JsonResponse
({
'ErrMsg'
:
_
(
'There is already a course defined with the same '
'ErrMsg'
:
_
(
'There is already a course defined with the same '
'organization, course number, and course run. Please '
'organization, course number, and course run. Please '
'change either organization or course number to be '
'change either organization or course number to be '
'unique.'
),
'unique.'
'OrgErrMsg'
:
_
(
'Please change either the organization or '
),
'course number so that it is unique.'
),
'OrgErrMsg'
:
_
(
'CourseErrMsg'
:
_
(
'Please change either the organization or '
'Please change either the organization or '
'course number so that it is unique.'
),
'course number so that it is unique.'
),
'CourseErrMsg'
:
_
(
'Please change either the organization or '
'course number so that it is unique.'
),
})
})
# dhm: this query breaks the abstraction, but I'll fix it when I do my suspended refactoring of this
# dhm: this query breaks the abstraction, but I'll fix it when I do my suspended refactoring of this
...
@@ -250,12 +254,15 @@ def create_new_course(request):
...
@@ -250,12 +254,15 @@ def create_new_course(request):
courses
=
modulestore
()
.
collection
.
find
(
course_search_location
,
fields
=
(
'_id'
))
courses
=
modulestore
()
.
collection
.
find
(
course_search_location
,
fields
=
(
'_id'
))
if
courses
.
count
()
>
0
:
if
courses
.
count
()
>
0
:
return
JsonResponse
({
return
JsonResponse
({
'ErrMsg'
:
_
(
'There is already a course defined with the same '
'ErrMsg'
:
_
(
'There is already a course defined with the same '
'organization and course number. Please '
'organization and course number. Please '
'change at least one field to be unique.'
),
'change at least one field to be unique.'
),
'OrgErrMsg'
:
_
(
'Please change either the organization or '
'OrgErrMsg'
:
_
(
'Please change either the organization or '
'course number so that it is unique.'
),
'course number so that it is unique.'
),
'CourseErrMsg'
:
_
(
'Please change either the organization or '
'CourseErrMsg'
:
_
(
'Please change either the organization or '
'course number so that it is unique.'
),
'course number so that it is unique.'
),
})
})
...
@@ -343,9 +350,8 @@ def course_info_handler(request, tag=None, course_id=None, branch=None, version_
...
@@ -343,9 +350,8 @@ def course_info_handler(request, tag=None, course_id=None, branch=None, version_
@ensure_csrf_cookie
@ensure_csrf_cookie
@require_http_methods
((
"GET"
,
"POST"
,
"PUT"
,
"DELETE"
))
@require_http_methods
((
"GET"
,
"POST"
,
"PUT"
,
"DELETE"
))
@expect_json
@expect_json
def
course_info_update_handler
(
def
course_info_update_handler
(
request
,
tag
=
None
,
course_id
=
None
,
branch
=
None
,
version_guid
=
None
,
block
=
None
,
request
,
tag
=
None
,
course_id
=
None
,
branch
=
None
,
version_guid
=
None
,
block
=
None
,
provided_id
=
None
provided_id
=
None
):
):
"""
"""
restful CRUD operations on course_info updates.
restful CRUD operations on course_info updates.
provided_id should be none if it's new (create) and index otherwise.
provided_id should be none if it's new (create) and index otherwise.
...
@@ -492,71 +498,25 @@ def grading_handler(request, tag=None, course_id=None, branch=None, version_guid
...
@@ -492,71 +498,25 @@ def grading_handler(request, tag=None, course_id=None, branch=None, version_guid
return
JsonResponse
()
return
JsonResponse
()
@login_required
# pylint: disable=invalid-name
@ensure_csrf_cookie
def
_config_course_advanced_components
(
request
,
course_module
):
def
course_config_advanced_page
(
request
,
org
,
course
,
name
):
"""
"""
Send models and views as well as html for editing the advanced course
Check to see if the user instantiated any advanced components. This
settings to the client.
is a hack that does the following :
1) adds/removes the open ended panel tab to a course automatically
org, course, name: Attributes of the Location for the item to edit
if the user has indicated that they want to edit the
combinedopendended or peergrading module
2) adds/removes the notes panel tab to a course automatically if
the user has indicated that they want the notes module enabled in
their course
"""
"""
location
=
get_location_and_verify_access
(
request
,
org
,
course
,
name
)
course_module
=
modulestore
()
.
get_item
(
location
)
return
render_to_response
(
'settings_advanced.html'
,
{
'context_course'
:
course_module
,
'course_location'
:
location
,
'course_locator'
:
loc_mapper
()
.
translate_location
(
location
.
course_id
,
location
,
False
,
True
),
'advanced_dict'
:
json
.
dumps
(
CourseMetadata
.
fetch
(
location
)),
})
@require_http_methods
((
"GET"
,
"POST"
,
"PUT"
,
"DELETE"
))
@login_required
@ensure_csrf_cookie
@expect_json
def
course_advanced_updates
(
request
,
org
,
course
,
name
):
"""
Restful CRUD operations on metadata. The payload is a json rep of the
metadata dicts. For delete, otoh, the payload is either a key or a list of
keys to delete.
org, course: Attributes of the Location for the item to edit
"""
location
=
get_location_and_verify_access
(
request
,
org
,
course
,
name
)
if
request
.
method
==
'GET'
:
return
JsonResponse
(
CourseMetadata
.
fetch
(
location
))
elif
request
.
method
==
'DELETE'
:
return
JsonResponse
(
CourseMetadata
.
delete_key
(
location
,
json
.
loads
(
request
.
body
)
))
else
:
# Whether or not to filter the tabs key out of the settings metadata
filter_tabs
=
True
# Check to see if the user instantiated any advanced components. This
# is a hack that does the following :
# 1) adds/removes the open ended panel tab to a course automatically
# if the user has indicated that they want to edit the
# combinedopendended or peergrading module
# 2) adds/removes the notes panel tab to a course automatically if
# the user has indicated that they want the notes module enabled in
# their course
# TODO refactor the above into distinct advanced policy settings
# TODO refactor the above into distinct advanced policy settings
if
ADVANCED_COMPONENT_POLICY_KEY
in
request
.
json
:
filter_tabs
=
True
# Exceptional conditions will pull this to False
# Get the course so that we can scrape current tabs
if
ADVANCED_COMPONENT_POLICY_KEY
in
request
.
json
:
# Maps tab types to components
course_module
=
modulestore
()
.
get_item
(
location
)
# Maps tab types to components
tab_component_map
=
{
tab_component_map
=
{
'open_ended'
:
OPEN_ENDED_COMPONENT_TYPES
,
'open_ended'
:
OPEN_ENDED_COMPONENT_TYPES
,
'notes'
:
NOTE_COMPONENT_TYPES
,
'notes'
:
NOTE_COMPONENT_TYPES
,
}
}
# Check to see if the user instantiated any notes or open ended
# Check to see if the user instantiated any notes or open ended
# components
# components
for
tab_type
in
tab_component_map
.
keys
():
for
tab_type
in
tab_component_map
.
keys
():
...
@@ -565,10 +525,7 @@ def course_advanced_updates(request, org, course, name):
...
@@ -565,10 +525,7 @@ def course_advanced_updates(request, org, course, name):
for
ac_type
in
component_types
:
for
ac_type
in
component_types
:
if
ac_type
in
request
.
json
[
ADVANCED_COMPONENT_POLICY_KEY
]:
if
ac_type
in
request
.
json
[
ADVANCED_COMPONENT_POLICY_KEY
]:
# Add tab to the course if needed
# Add tab to the course if needed
changed
,
new_tabs
=
add_extra_panel_tab
(
changed
,
new_tabs
=
add_extra_panel_tab
(
tab_type
,
course_module
)
tab_type
,
course_module
)
# If a tab has been added to the course, then send the
# If a tab has been added to the course, then send the
# metadata along to CourseMetadata.update_from_json
# metadata along to CourseMetadata.update_from_json
if
changed
:
if
changed
:
...
@@ -576,32 +533,67 @@ def course_advanced_updates(request, org, course, name):
...
@@ -576,32 +533,67 @@ def course_advanced_updates(request, org, course, name):
request
.
json
.
update
({
'tabs'
:
new_tabs
})
request
.
json
.
update
({
'tabs'
:
new_tabs
})
# Indicate that tabs should not be filtered out of
# Indicate that tabs should not be filtered out of
# the metadata
# the metadata
filter_tabs
=
False
filter_tabs
=
False
# Set this flag to avoid the tab removal code below.
# Set this flag to avoid the tab removal code below.
found_ac_type
=
True
#break
found_ac_type
=
True
break
# If we did not find a module type in the advanced settings,
# If we did not find a module type in the advanced settings,
# we may need to remove the tab from the course.
# we may need to remove the tab from the course.
if
not
found_ac_type
:
if
not
found_ac_type
:
# Remove tab from the course if needed
# Remove tab from the course if needed
changed
,
new_tabs
=
remove_extra_panel_tab
(
tab_type
,
course_module
)
changed
,
new_tabs
=
remove_extra_panel_tab
(
tab_type
,
course_module
)
if
changed
:
if
changed
:
course_module
.
tabs
=
new_tabs
course_module
.
tabs
=
new_tabs
request
.
json
.
update
({
'tabs'
:
new_tabs
})
request
.
json
.
update
({
'tabs'
:
new_tabs
})
# Indicate that tabs should *not* be filtered out of
# Indicate that tabs should *not* be filtered out of
# the metadata
# the metadata
filter_tabs
=
False
filter_tabs
=
False
return
filter_tabs
@login_required
@ensure_csrf_cookie
@require_http_methods
((
"GET"
,
"POST"
,
"PUT"
))
@expect_json
def
advanced_settings_handler
(
request
,
course_id
=
None
,
branch
=
None
,
version_guid
=
None
,
block
=
None
,
tag
=
None
):
"""
Course settings configuration
GET
html: get the page
json: get the model
PUT, POST
json: update the Course's settings. The payload is a json rep of the
metadata dicts. The dict can include a "unsetKeys" entry which is a list
of keys whose values to unset: i.e., revert to default
"""
locator
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
usage_id
=
block
)
if
not
has_access
(
request
.
user
,
locator
):
raise
PermissionDenied
()
course_old_location
=
loc_mapper
()
.
translate_locator_to_location
(
locator
)
course_module
=
modulestore
()
.
get_item
(
course_old_location
)
if
'text/html'
in
request
.
META
.
get
(
'HTTP_ACCEPT'
,
''
)
and
request
.
method
==
'GET'
:
return
render_to_response
(
'settings_advanced.html'
,
{
'context_course'
:
course_module
,
'advanced_dict'
:
json
.
dumps
(
CourseMetadata
.
fetch
(
course_module
)),
'advanced_settings_url'
:
locator
.
url_reverse
(
'settings/advanced'
)
})
elif
'application/json'
in
request
.
META
.
get
(
'HTTP_ACCEPT'
,
''
):
if
request
.
method
==
'GET'
:
return
JsonResponse
(
CourseMetadata
.
fetch
(
course_module
))
else
:
# Whether or not to filter the tabs key out of the settings metadata
filter_tabs
=
_config_course_advanced_components
(
request
,
course_module
)
try
:
try
:
return
JsonResponse
(
CourseMetadata
.
update_from_json
(
return
JsonResponse
(
CourseMetadata
.
update_from_json
(
location
,
course_module
,
request
.
json
,
request
.
json
,
filter_tabs
=
filter_tabs
filter_tabs
=
filter_tabs
))
))
except
(
TypeError
,
ValueError
)
as
err
:
except
(
TypeError
,
ValueError
)
as
err
:
return
HttpResponseBadRequest
(
return
HttpResponseBadRequest
(
"Incorrect setting format. "
+
str
(
err
),
"Incorrect setting format. {}"
.
format
(
err
),
content_type
=
"text/plain"
content_type
=
"text/plain"
)
)
...
...
cms/djangoapps/models/settings/course_details.py
View file @
38fba248
...
@@ -32,11 +32,11 @@ class CourseDetails(object):
...
@@ -32,11 +32,11 @@ class CourseDetails(object):
self
.
course_image_asset_path
=
""
# URL of the course image
self
.
course_image_asset_path
=
""
# URL of the course image
@classmethod
@classmethod
def
fetch
(
cls
,
course_locat
ion
):
def
fetch
(
cls
,
course_locat
or
):
"""
"""
Fetch the course details for the given course from persistence and return a CourseDetails model.
Fetch the course details for the given course from persistence and return a CourseDetails model.
"""
"""
course_old_location
=
loc_mapper
()
.
translate_locator_to_location
(
course_locat
ion
)
course_old_location
=
loc_mapper
()
.
translate_locator_to_location
(
course_locat
or
)
descriptor
=
get_modulestore
(
course_old_location
)
.
get_item
(
course_old_location
)
descriptor
=
get_modulestore
(
course_old_location
)
.
get_item
(
course_old_location
)
course
=
cls
(
course_old_location
.
org
,
course_old_location
.
course
,
course_old_location
.
name
)
course
=
cls
(
course_old_location
.
org
,
course_old_location
.
course
,
course_old_location
.
name
)
...
@@ -75,11 +75,11 @@ class CourseDetails(object):
...
@@ -75,11 +75,11 @@ class CourseDetails(object):
return
course
return
course
@classmethod
@classmethod
def
update_from_json
(
cls
,
course_locat
ion
,
jsondict
):
def
update_from_json
(
cls
,
course_locat
or
,
jsondict
):
"""
"""
Decode the json into CourseDetails and save any changed attrs to the db
Decode the json into CourseDetails and save any changed attrs to the db
"""
"""
course_old_location
=
loc_mapper
()
.
translate_locator_to_location
(
course_locat
ion
)
course_old_location
=
loc_mapper
()
.
translate_locator_to_location
(
course_locat
or
)
descriptor
=
get_modulestore
(
course_old_location
)
.
get_item
(
course_old_location
)
descriptor
=
get_modulestore
(
course_old_location
)
.
get_item
(
course_old_location
)
dirty
=
False
dirty
=
False
...
@@ -153,7 +153,7 @@ class CourseDetails(object):
...
@@ -153,7 +153,7 @@ class CourseDetails(object):
# Could just return jsondict w/o doing any db reads, but I put the reads in as a means to confirm
# Could just return jsondict w/o doing any db reads, but I put the reads in as a means to confirm
# it persisted correctly
# it persisted correctly
return
CourseDetails
.
fetch
(
course_locat
ion
)
return
CourseDetails
.
fetch
(
course_locat
or
)
@staticmethod
@staticmethod
def
parse_video_tag
(
raw_video
):
def
parse_video_tag
(
raw_video
):
...
...
cms/djangoapps/models/settings/course_grading.py
View file @
38fba248
...
@@ -18,11 +18,11 @@ class CourseGradingModel(object):
...
@@ -18,11 +18,11 @@ class CourseGradingModel(object):
self
.
grace_period
=
CourseGradingModel
.
convert_set_grace_period
(
course_descriptor
)
self
.
grace_period
=
CourseGradingModel
.
convert_set_grace_period
(
course_descriptor
)
@classmethod
@classmethod
def
fetch
(
cls
,
course_locat
ion
):
def
fetch
(
cls
,
course_locat
or
):
"""
"""
Fetch the course grading policy for the given course from persistence and return a CourseGradingModel.
Fetch the course grading policy for the given course from persistence and return a CourseGradingModel.
"""
"""
course_old_location
=
loc_mapper
()
.
translate_locator_to_location
(
course_locat
ion
)
course_old_location
=
loc_mapper
()
.
translate_locator_to_location
(
course_locat
or
)
descriptor
=
get_modulestore
(
course_old_location
)
.
get_item
(
course_old_location
)
descriptor
=
get_modulestore
(
course_old_location
)
.
get_item
(
course_old_location
)
model
=
cls
(
descriptor
)
model
=
cls
(
descriptor
)
...
@@ -52,12 +52,12 @@ class CourseGradingModel(object):
...
@@ -52,12 +52,12 @@ class CourseGradingModel(object):
}
}
@staticmethod
@staticmethod
def
update_from_json
(
course_locat
ion
,
jsondict
):
def
update_from_json
(
course_locat
or
,
jsondict
):
"""
"""
Decode the json into CourseGradingModel and save any changes. Returns the modified model.
Decode the json into CourseGradingModel and save any changes. Returns the modified model.
Probably not the usual path for updates as it's too coarse grained.
Probably not the usual path for updates as it's too coarse grained.
"""
"""
course_old_location
=
loc_mapper
()
.
translate_locator_to_location
(
course_locat
ion
)
course_old_location
=
loc_mapper
()
.
translate_locator_to_location
(
course_locat
or
)
descriptor
=
get_modulestore
(
course_old_location
)
.
get_item
(
course_old_location
)
descriptor
=
get_modulestore
(
course_old_location
)
.
get_item
(
course_old_location
)
graders_parsed
=
[
CourseGradingModel
.
parse_grader
(
jsonele
)
for
jsonele
in
jsondict
[
'graders'
]]
graders_parsed
=
[
CourseGradingModel
.
parse_grader
(
jsonele
)
for
jsonele
in
jsondict
[
'graders'
]]
...
@@ -69,9 +69,9 @@ class CourseGradingModel(object):
...
@@ -69,9 +69,9 @@ class CourseGradingModel(object):
course_old_location
,
descriptor
.
get_explicitly_set_fields_by_scope
(
Scope
.
content
)
course_old_location
,
descriptor
.
get_explicitly_set_fields_by_scope
(
Scope
.
content
)
)
)
CourseGradingModel
.
update_grace_period_from_json
(
course_locat
ion
,
jsondict
[
'grace_period'
])
CourseGradingModel
.
update_grace_period_from_json
(
course_locat
or
,
jsondict
[
'grace_period'
])
return
CourseGradingModel
.
fetch
(
course_locat
ion
)
return
CourseGradingModel
.
fetch
(
course_locat
or
)
@staticmethod
@staticmethod
def
update_grader_from_json
(
course_location
,
grader
):
def
update_grader_from_json
(
course_location
,
grader
):
...
...
cms/djangoapps/models/settings/course_metadata.py
View file @
38fba248
from
xmodule.modulestore
import
Location
from
xblock.fields
import
Scope
from
contentstore.utils
import
get_modulestore
from
contentstore.utils
import
get_modulestore
from
xmodule.modulestore.inheritance
import
own_metadata
from
xmodule.modulestore.inheritance
import
own_metadata
from
xblock.fields
import
Scope
from
cms.xmodule_namespace
import
CmsBlockMixin
from
cms.xmodule_namespace
import
CmsBlockMixin
...
@@ -20,21 +20,18 @@ class CourseMetadata(object):
...
@@ -20,21 +20,18 @@ class CourseMetadata(object):
'tabs'
,
'tabs'
,
'graceperiod'
,
'graceperiod'
,
'checklists'
,
'checklists'
,
'show_timezone'
'show_timezone'
,
'format'
,
'graded'
,
]
]
@classmethod
@classmethod
def
fetch
(
cls
,
course_location
):
def
fetch
(
cls
,
descriptor
):
"""
"""
Fetch the key:value editable course details for the given course from
Fetch the key:value editable course details for the given course from
persistence and return a CourseMetadata model.
persistence and return a CourseMetadata model.
"""
"""
if
not
isinstance
(
course_location
,
Location
):
result
=
{}
course_location
=
Location
(
course_location
)
course
=
{}
descriptor
=
get_modulestore
(
course_location
)
.
get_item
(
course_location
)
for
field
in
descriptor
.
fields
.
values
():
for
field
in
descriptor
.
fields
.
values
():
if
field
.
name
in
CmsBlockMixin
.
fields
:
if
field
.
name
in
CmsBlockMixin
.
fields
:
...
@@ -46,19 +43,17 @@ class CourseMetadata(object):
...
@@ -46,19 +43,17 @@ class CourseMetadata(object):
if
field
.
name
in
cls
.
FILTERED_LIST
:
if
field
.
name
in
cls
.
FILTERED_LIST
:
continue
continue
course
[
field
.
name
]
=
field
.
read_json
(
descriptor
)
result
[
field
.
name
]
=
field
.
read_json
(
descriptor
)
return
course
return
result
@classmethod
@classmethod
def
update_from_json
(
cls
,
course_location
,
jsondict
,
filter_tabs
=
True
):
def
update_from_json
(
cls
,
descriptor
,
jsondict
,
filter_tabs
=
True
):
"""
"""
Decode the json into CourseMetadata and save any changed attrs to the db.
Decode the json into CourseMetadata and save any changed attrs to the db.
Ensures none of the fields are in the blacklist.
Ensures none of the fields are in the blacklist.
"""
"""
descriptor
=
get_modulestore
(
course_location
)
.
get_item
(
course_location
)
dirty
=
False
dirty
=
False
# Copy the filtered list to avoid permanently changing the class attribute.
# Copy the filtered list to avoid permanently changing the class attribute.
...
@@ -72,39 +67,17 @@ class CourseMetadata(object):
...
@@ -72,39 +67,17 @@ class CourseMetadata(object):
if
key
in
filtered_list
:
if
key
in
filtered_list
:
continue
continue
if
key
==
"unsetKeys"
:
dirty
=
True
for
unset
in
val
:
descriptor
.
fields
[
unset
]
.
delete_from
(
descriptor
)
if
hasattr
(
descriptor
,
key
)
and
getattr
(
descriptor
,
key
)
!=
val
:
if
hasattr
(
descriptor
,
key
)
and
getattr
(
descriptor
,
key
)
!=
val
:
dirty
=
True
dirty
=
True
value
=
descriptor
.
fields
[
key
]
.
from_json
(
val
)
value
=
descriptor
.
fields
[
key
]
.
from_json
(
val
)
setattr
(
descriptor
,
key
,
value
)
setattr
(
descriptor
,
key
,
value
)
if
dirty
:
if
dirty
:
# Save the data that we've just changed to the underlying
get_modulestore
(
descriptor
.
location
)
.
update_metadata
(
descriptor
.
location
,
own_metadata
(
descriptor
))
# MongoKeyValueStore before we update the mongo datastore.
descriptor
.
save
()
get_modulestore
(
course_location
)
.
update_metadata
(
course_location
,
own_metadata
(
descriptor
))
# Could just generate and return a course obj w/o doing any db reads,
# but I put the reads in as a means to confirm it persisted correctly
return
cls
.
fetch
(
course_location
)
@classmethod
def
delete_key
(
cls
,
course_location
,
payload
):
'''
Remove the given metadata key(s) from the course. payload can be a
single key or [key..]
'''
descriptor
=
get_modulestore
(
course_location
)
.
get_item
(
course_location
)
for
key
in
payload
[
'deleteKeys'
]:
if
hasattr
(
descriptor
,
key
):
delattr
(
descriptor
,
key
)
# Save the data that we've just changed to the underlying
# MongoKeyValueStore before we update the mongo datastore.
descriptor
.
save
()
get_modulestore
(
course_location
)
.
update_metadata
(
course_location
,
own_metadata
(
descriptor
))
return
cls
.
fetch
(
course_location
)
return
cls
.
fetch
(
descriptor
)
cms/templates/settings.html
View file @
38fba248
...
@@ -6,7 +6,6 @@
...
@@ -6,7 +6,6 @@
<
%!
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
from
django
.
utils
.
translation
import
ugettext
as
_
from
xmodule
.
modulestore
.
django
import
loc_mapper
from
xmodule
.
modulestore
.
django
import
loc_mapper
from
django
.
core
.
urlresolvers
import
reverse
%
>
%
>
<
%
block
name=
"jsextra"
>
<
%
block
name=
"jsextra"
>
...
@@ -293,14 +292,14 @@ require(["domReady!", "jquery", "js/models/settings/course_details", "js/views/s
...
@@ -293,14 +292,14 @@ require(["domReady!", "jquery", "js/models/settings/course_details", "js/views/s
<
%
<
%
course_team_url =
course_locator.url_reverse('course_team/',
'')
course_team_url =
course_locator.url_reverse('course_team/',
'')
grading_config_url =
course_locator.url_reverse('settings/grading/')
grading_config_url =
course_locator.url_reverse('settings/grading/')
ctx_loc =
context_course.location
advanced_config_url =
course_locator.url_reverse('settings/advanced/')
%
>
%
>
<h3
class=
"title-3"
>
${_("Other Course Settings")}
</h3>
<h3
class=
"title-3"
>
${_("Other Course Settings")}
</h3>
<nav
class=
"nav-related"
>
<nav
class=
"nav-related"
>
<ul>
<ul>
<li
class=
"nav-item"
><a
href=
"${grading_config_url}"
>
${_("Grading")}
</a></li>
<li
class=
"nav-item"
><a
href=
"${grading_config_url}"
>
${_("Grading")}
</a></li>
<li
class=
"nav-item"
><a
href=
"${course_team_url}"
>
${_("Course Team")}
</a></li>
<li
class=
"nav-item"
><a
href=
"${course_team_url}"
>
${_("Course Team")}
</a></li>
<li
class=
"nav-item"
><a
href=
"${
reverse('course_advanced_settings', kwargs={'org' : ctx_loc.org, 'course' : ctx_loc.course, 'name': ctx_loc.name})
}"
>
${_("Advanced Settings")}
</a></li>
<li
class=
"nav-item"
><a
href=
"${
advanced_config_url
}"
>
${_("Advanced Settings")}
</a></li>
</ul>
</ul>
</nav>
</nav>
% endif
% endif
...
...
cms/templates/settings_advanced.html
View file @
38fba248
<
%
inherit
file=
"base.html"
/>
<
%
inherit
file=
"base.html"
/>
<
%
namespace
name=
'static'
file=
'static_content.html'
/>
<
%
namespace
name=
'static'
file=
'static_content.html'
/>
<
%!
<
%!
from
django
.
core
.
urlresolvers
import
reverse
from
django
.
utils
.
translation
import
ugettext
as
_
from
django
.
utils
.
translation
import
ugettext
as
_
from
contentstore
import
utils
from
contentstore
import
utils
from
xmodule
.
modulestore
.
django
import
loc_mapper
from
xmodule
.
modulestore
.
django
import
loc_mapper
from
django
.
core
.
urlresolvers
import
reverse
%
>
%
>
<
%
block
name=
"title"
>
${_("Advanced Settings")}
</
%
block>
<
%
block
name=
"title"
>
${_("Advanced Settings")}
</
%
block>
<
%
block
name=
"bodyclass"
>
is-signedin course advanced view-settings
</
%
block>
<
%
block
name=
"bodyclass"
>
is-signedin course advanced view-settings
</
%
block>
...
@@ -28,7 +26,7 @@ require(["domReady!", "jquery", "js/models/settings/advanced", "js/views/setting
...
@@ -28,7 +26,7 @@ require(["domReady!", "jquery", "js/models/settings/advanced", "js/views/setting
// proactively populate advanced b/c it has the filtered list and doesn't really follow the model pattern
// proactively populate advanced b/c it has the filtered list and doesn't really follow the model pattern
var
advancedModel
=
new
AdvancedSettingsModel
(
$
{
advanced_dict
|
n
},
{
parse
:
true
});
var
advancedModel
=
new
AdvancedSettingsModel
(
$
{
advanced_dict
|
n
},
{
parse
:
true
});
advancedModel
.
url
=
"${
reverse('course_advanced_settings_updates', kwargs=dict(org=context_course.location.org, course=context_course.location.course, name=context_course.location.name))
}"
;
advancedModel
.
url
=
"${
advanced_settings_url
}"
;
var
editor
=
new
AdvancedSettingsView
({
var
editor
=
new
AdvancedSettingsView
({
el
:
$
(
'.settings-advanced'
),
el
:
$
(
'.settings-advanced'
),
...
@@ -91,13 +89,15 @@ require(["domReady!", "jquery", "js/models/settings/advanced", "js/views/setting
...
@@ -91,13 +89,15 @@ require(["domReady!", "jquery", "js/models/settings/advanced", "js/views/setting
<
%
<
%
ctx_loc =
context_course.location
ctx_loc =
context_course.location
location =
loc_mapper().translate_location(ctx_loc.course_id,
ctx_loc
,
False
,
True
)
location =
loc_mapper().translate_location(ctx_loc.course_id,
ctx_loc
,
False
,
True
)
details_url =
location.url_reverse('settings/details/')
grading_url =
location.url_reverse('settings/grading/')
course_team_url =
location.url_reverse('course_team/',
'')
course_team_url =
location.url_reverse('course_team/',
'')
%
>
%
>
<h3
class=
"title-3"
>
${_("Other Course Settings")}
</h3>
<h3
class=
"title-3"
>
${_("Other Course Settings")}
</h3>
<nav
class=
"nav-related"
>
<nav
class=
"nav-related"
>
<ul>
<ul>
<li
class=
"nav-item"
><a
href=
"${
course_locator.url_reverse('settings/details/')
}"
>
${_("Details
&
Schedule")}
</a></li>
<li
class=
"nav-item"
><a
href=
"${
details_url
}"
>
${_("Details
&
Schedule")}
</a></li>
<li
class=
"nav-item"
><a
href=
"${
course_locator.url_reverse('settings/grading/')
}"
>
${_("Grading")}
</a></li>
<li
class=
"nav-item"
><a
href=
"${
grading_url
}"
>
${_("Grading")}
</a></li>
<li
class=
"nav-item"
><a
href=
"${course_team_url}"
>
${_("Course Team")}
</a></li>
<li
class=
"nav-item"
><a
href=
"${course_team_url}"
>
${_("Course Team")}
</a></li>
</ul>
</ul>
</nav>
</nav>
...
...
cms/templates/settings_graders.html
View file @
38fba248
...
@@ -7,7 +7,6 @@
...
@@ -7,7 +7,6 @@
from
contentstore
import
utils
from
contentstore
import
utils
from
django
.
utils
.
translation
import
ugettext
as
_
from
django
.
utils
.
translation
import
ugettext
as
_
from
xmodule
.
modulestore
.
django
import
loc_mapper
from
xmodule
.
modulestore
.
django
import
loc_mapper
from
django
.
core
.
urlresolvers
import
reverse
%
>
%
>
<
%
block
name=
"header_extras"
>
<
%
block
name=
"header_extras"
>
...
@@ -139,15 +138,16 @@ require(["domReady!", "jquery", "js/views/settings/grading", "js/models/settings
...
@@ -139,15 +138,16 @@ require(["domReady!", "jquery", "js/views/settings/grading", "js/models/settings
<div
class=
"bit"
>
<div
class=
"bit"
>
% if context_course:
% if context_course:
<
%
<
%
ctx_loc =
context_course.location
course_team_url =
course_locator.url_reverse('course_team/')
course_team_url =
course_locator.url_reverse('course_team/')
advanced_settings_url =
course_locator.url_reverse('settings/advanced/')
detailed_settings_url =
course_locator.url_reverse('settings/details/')
%
>
%
>
<h3
class=
"title-3"
>
${_("Other Course Settings")}
</h3>
<h3
class=
"title-3"
>
${_("Other Course Settings")}
</h3>
<nav
class=
"nav-related"
>
<nav
class=
"nav-related"
>
<ul>
<ul>
<li
class=
"nav-item"
><a
href=
"${
course_locator.url_reverse('settings/details/')
}"
>
${_("Details
&
Schedule")}
</a></li>
<li
class=
"nav-item"
><a
href=
"${
detailed_settings_url
}"
>
${_("Details
&
Schedule")}
</a></li>
<li
class=
"nav-item"
><a
href=
"${course_team_url}"
>
${_("Course Team")}
</a></li>
<li
class=
"nav-item"
><a
href=
"${course_team_url}"
>
${_("Course Team")}
</a></li>
<li
class=
"nav-item"
><a
href=
"${
reverse('course_advanced_settings', kwargs={'org' : ctx_loc.org, 'course' : ctx_loc.course, 'name': ctx_loc.name})
}"
>
${_("Advanced Settings")}
</a></li>
<li
class=
"nav-item"
><a
href=
"${
advanced_settings_url
}"
>
${_("Advanced Settings")}
</a></li>
</ul>
</ul>
</nav>
</nav>
% endif
% endif
...
...
cms/templates/widgets/header.html
View file @
38fba248
...
@@ -25,6 +25,7 @@
...
@@ -25,6 +25,7 @@
export_url =
location.url_reverse('export')
export_url =
location.url_reverse('export')
settings_url =
location.url_reverse('settings/details/')
settings_url =
location.url_reverse('settings/details/')
grading_url =
location.url_reverse('settings/grading/')
grading_url =
location.url_reverse('settings/grading/')
advanced_settings_url =
location.url_reverse('settings/advanced/')
tabs_url =
location.url_reverse('tabs')
tabs_url =
location.url_reverse('tabs')
%
>
%
>
<h2
class=
"info-course"
>
<h2
class=
"info-course"
>
...
@@ -80,7 +81,7 @@
...
@@ -80,7 +81,7 @@
<a
href=
"${course_team_url}"
>
${_("Course Team")}
</a>
<a
href=
"${course_team_url}"
>
${_("Course Team")}
</a>
</li>
</li>
<li
class=
"nav-item nav-course-settings-advanced"
>
<li
class=
"nav-item nav-course-settings-advanced"
>
<a
href=
"${
reverse('course_advanced_settings', kwargs={'org' : ctx_loc.org, 'course' : ctx_loc.course, 'name': ctx_loc.name})
}"
>
${_("Advanced Settings")}
</a>
<a
href=
"${
advanced_settings_url
}"
>
${_("Advanced Settings")}
</a>
</li>
</li>
</ul>
</ul>
</div>
</div>
...
...
cms/urls.py
View file @
38fba248
...
@@ -23,13 +23,6 @@ urlpatterns = patterns('', # nopep8
...
@@ -23,13 +23,6 @@ urlpatterns = patterns('', # nopep8
url
(
r'^preview/xblock/(?P<usage_id>.*?)/handler/(?P<handler>[^/]*)(?:/(?P<suffix>[^/]*))?$'
,
url
(
r'^preview/xblock/(?P<usage_id>.*?)/handler/(?P<handler>[^/]*)(?:/(?P<suffix>[^/]*))?$'
,
'contentstore.views.preview_handler'
,
name
=
'preview_handler'
),
'contentstore.views.preview_handler'
,
name
=
'preview_handler'
),
# This is the URL to initially render the course advanced settings.
url
(
r'^(?P<org>[^/]+)/(?P<course>[^/]+)/settings-advanced/(?P<name>[^/]+)$'
,
'contentstore.views.course_config_advanced_page'
,
name
=
'course_advanced_settings'
),
# This is the URL used by BackBone for updating and re-fetching the model.
url
(
r'^(?P<org>[^/]+)/(?P<course>[^/]+)/settings-advanced/(?P<name>[^/]+)/update.*$'
,
'contentstore.views.course_advanced_updates'
,
name
=
'course_advanced_settings_updates'
),
url
(
r'^(?P<org>[^/]+)/(?P<course>[^/]+)/textbooks/(?P<name>[^/]+)$'
,
url
(
r'^(?P<org>[^/]+)/(?P<course>[^/]+)/textbooks/(?P<name>[^/]+)$'
,
'contentstore.views.textbook_index'
,
name
=
'textbook_index'
),
'contentstore.views.textbook_index'
,
name
=
'textbook_index'
),
url
(
r'^(?P<org>[^/]+)/(?P<course>[^/]+)/textbooks/(?P<name>[^/]+)/new$'
,
url
(
r'^(?P<org>[^/]+)/(?P<course>[^/]+)/textbooks/(?P<name>[^/]+)/new$'
,
...
@@ -95,6 +88,7 @@ urlpatterns += patterns(
...
@@ -95,6 +88,7 @@ urlpatterns += patterns(
url
(
r'(?ix)^tabs/{}$'
.
format
(
parsers
.
URL_RE_SOURCE
),
'tabs_handler'
),
url
(
r'(?ix)^tabs/{}$'
.
format
(
parsers
.
URL_RE_SOURCE
),
'tabs_handler'
),
url
(
r'(?ix)^settings/details/{}$'
.
format
(
parsers
.
URL_RE_SOURCE
),
'settings_handler'
),
url
(
r'(?ix)^settings/details/{}$'
.
format
(
parsers
.
URL_RE_SOURCE
),
'settings_handler'
),
url
(
r'(?ix)^settings/grading/{}(/)?(?P<grader_index>\d+)?$'
.
format
(
parsers
.
URL_RE_SOURCE
),
'grading_handler'
),
url
(
r'(?ix)^settings/grading/{}(/)?(?P<grader_index>\d+)?$'
.
format
(
parsers
.
URL_RE_SOURCE
),
'grading_handler'
),
url
(
r'(?ix)^settings/advanced/{}$'
.
format
(
parsers
.
URL_RE_SOURCE
),
'advanced_settings_handler'
),
)
)
js_info_dict
=
{
js_info_dict
=
{
...
...
common/djangoapps/util/json_request.py
View file @
38fba248
from
functools
import
wraps
from
functools
import
wraps
import
copy
import
json
import
json
from
django.core.serializers
import
serialize
from
django.core.serializers
import
serialize
from
django.core.serializers.json
import
DjangoJSONEncoder
from
django.core.serializers.json
import
DjangoJSONEncoder
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment