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
0d349037
Commit
0d349037
authored
Nov 25, 2013
by
David Baumgold
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactor textbooks to use locator URLs
STUD-945
parent
f7d86bff
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
174 additions
and
239 deletions
+174
-239
cms/djangoapps/contentstore/tests/test_contentstore.py
+1
-8
cms/djangoapps/contentstore/tests/test_textbooks.py
+13
-50
cms/djangoapps/contentstore/tests/utils.py
+2
-1
cms/djangoapps/contentstore/views/course.py
+137
-150
cms/static/coffee/spec/main_spec.coffee
+1
-1
cms/static/coffee/spec/models/textbook_spec.coffee
+9
-8
cms/static/coffee/spec/views/textbook_spec.coffee
+2
-0
cms/static/js/collections/textbook.js
+1
-4
cms/static/js/models/textbook.js
+3
-8
cms/templates/widgets/header.html
+3
-2
cms/urls.py
+2
-7
No files found.
cms/djangoapps/contentstore/tests/test_contentstore.py
View file @
0d349037
...
@@ -1659,14 +1659,7 @@ class ContentStoreTest(ModuleStoreTestCase):
...
@@ -1659,14 +1659,7 @@ class ContentStoreTest(ModuleStoreTestCase):
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'
)
test_get_html
(
'settings/advanced'
)
test_get_html
(
'textbooks'
)
# textbook index
resp
=
self
.
client
.
get_html
(
reverse
(
'textbook_index'
,
kwargs
=
{
'org'
:
loc
.
org
,
'course'
:
loc
.
course
,
'name'
:
loc
.
name
}))
self
.
assertEqual
(
resp
.
status_code
,
200
)
_test_no_locations
(
self
,
resp
)
# go look at a subsection page
# go look at a subsection page
subsection_location
=
loc
.
replace
(
category
=
'sequential'
,
name
=
'test_sequence'
)
subsection_location
=
loc
.
replace
(
category
=
'sequential'
,
name
=
'test_sequence'
)
...
...
cms/djangoapps/contentstore/tests/test_textbooks.py
View file @
0d349037
...
@@ -14,11 +14,7 @@ class TextbookIndexTestCase(CourseTestCase):
...
@@ -14,11 +14,7 @@ class TextbookIndexTestCase(CourseTestCase):
def
setUp
(
self
):
def
setUp
(
self
):
"Set the URL for tests"
"Set the URL for tests"
super
(
TextbookIndexTestCase
,
self
)
.
setUp
()
super
(
TextbookIndexTestCase
,
self
)
.
setUp
()
self
.
url
=
reverse
(
'textbook_index'
,
kwargs
=
{
self
.
url
=
self
.
course_locator
.
url_reverse
(
'textbooks'
)
'org'
:
self
.
course
.
location
.
org
,
'course'
:
self
.
course
.
location
.
course
,
'name'
:
self
.
course
.
location
.
name
,
})
def
test_view_index
(
self
):
def
test_view_index
(
self
):
"Basic check that the textbook index page responds correctly"
"Basic check that the textbook index page responds correctly"
...
@@ -77,13 +73,13 @@ class TextbookIndexTestCase(CourseTestCase):
...
@@ -77,13 +73,13 @@ class TextbookIndexTestCase(CourseTestCase):
obj
=
json
.
loads
(
resp
.
content
)
obj
=
json
.
loads
(
resp
.
content
)
self
.
assertEqual
(
content
,
obj
)
self
.
assertEqual
(
content
,
obj
)
def
test_view_index_xhr_p
os
t
(
self
):
def
test_view_index_xhr_p
u
t
(
self
):
"Check that you can save information to the server"
"Check that you can save information to the server"
textbooks
=
[
textbooks
=
[
{
"tab_title"
:
"Hi, mom!"
},
{
"tab_title"
:
"Hi, mom!"
},
{
"tab_title"
:
"Textbook 2"
},
{
"tab_title"
:
"Textbook 2"
},
]
]
resp
=
self
.
client
.
p
os
t
(
resp
=
self
.
client
.
p
u
t
(
self
.
url
,
self
.
url
,
data
=
json
.
dumps
(
textbooks
),
data
=
json
.
dumps
(
textbooks
),
content_type
=
"application/json"
,
content_type
=
"application/json"
,
...
@@ -102,9 +98,9 @@ class TextbookIndexTestCase(CourseTestCase):
...
@@ -102,9 +98,9 @@ class TextbookIndexTestCase(CourseTestCase):
no_ids
.
append
(
textbook
)
no_ids
.
append
(
textbook
)
self
.
assertEqual
(
no_ids
,
textbooks
)
self
.
assertEqual
(
no_ids
,
textbooks
)
def
test_view_index_xhr_p
os
t_invalid
(
self
):
def
test_view_index_xhr_p
u
t_invalid
(
self
):
"Check that you can't save invalid JSON"
"Check that you can't save invalid JSON"
resp
=
self
.
client
.
p
os
t
(
resp
=
self
.
client
.
p
u
t
(
self
.
url
,
self
.
url
,
data
=
"invalid"
,
data
=
"invalid"
,
content_type
=
"application/json"
,
content_type
=
"application/json"
,
...
@@ -122,11 +118,7 @@ class TextbookCreateTestCase(CourseTestCase):
...
@@ -122,11 +118,7 @@ class TextbookCreateTestCase(CourseTestCase):
def
setUp
(
self
):
def
setUp
(
self
):
"Set up a url and some textbook content for tests"
"Set up a url and some textbook content for tests"
super
(
TextbookCreateTestCase
,
self
)
.
setUp
()
super
(
TextbookCreateTestCase
,
self
)
.
setUp
()
self
.
url
=
reverse
(
'create_textbook'
,
kwargs
=
{
self
.
url
=
self
.
course_locator
.
url_reverse
(
'textbooks'
)
'org'
:
self
.
course
.
location
.
org
,
'course'
:
self
.
course
.
location
.
course
,
'name'
:
self
.
course
.
location
.
name
,
})
self
.
textbook
=
{
self
.
textbook
=
{
"tab_title"
:
"Economics"
,
"tab_title"
:
"Economics"
,
"chapters"
:
{
"chapters"
:
{
...
@@ -151,15 +143,6 @@ class TextbookCreateTestCase(CourseTestCase):
...
@@ -151,15 +143,6 @@ class TextbookCreateTestCase(CourseTestCase):
del
textbook
[
"id"
]
del
textbook
[
"id"
]
self
.
assertEqual
(
self
.
textbook
,
textbook
)
self
.
assertEqual
(
self
.
textbook
,
textbook
)
def
test_get
(
self
):
"Test that GET is not allowed"
resp
=
self
.
client
.
get
(
self
.
url
,
HTTP_ACCEPT
=
"application/json"
,
HTTP_X_REQUESTED_WITH
=
"XMLHttpRequest"
,
)
self
.
assertEqual
(
resp
.
status_code
,
405
)
def
test_valid_id
(
self
):
def
test_valid_id
(
self
):
"Textbook IDs must begin with a number; try a valid one"
"Textbook IDs must begin with a number; try a valid one"
self
.
textbook
[
"id"
]
=
"7x5"
self
.
textbook
[
"id"
]
=
"7x5"
...
@@ -188,12 +171,12 @@ class TextbookCreateTestCase(CourseTestCase):
...
@@ -188,12 +171,12 @@ class TextbookCreateTestCase(CourseTestCase):
self
.
assertNotIn
(
"Location"
,
resp
)
self
.
assertNotIn
(
"Location"
,
resp
)
class
Textbook
ById
TestCase
(
CourseTestCase
):
class
Textbook
Detail
TestCase
(
CourseTestCase
):
"Test cases for the `textbook_
by_id
` view"
"Test cases for the `textbook_
detail_handler
` view"
def
setUp
(
self
):
def
setUp
(
self
):
"Set some useful content and URLs for tests"
"Set some useful content and URLs for tests"
super
(
Textbook
ById
TestCase
,
self
)
.
setUp
()
super
(
Textbook
Detail
TestCase
,
self
)
.
setUp
()
self
.
textbook1
=
{
self
.
textbook1
=
{
"tab_title"
:
"Economics"
,
"tab_title"
:
"Economics"
,
"id"
:
1
,
"id"
:
1
,
...
@@ -202,12 +185,7 @@ class TextbookByIdTestCase(CourseTestCase):
...
@@ -202,12 +185,7 @@ class TextbookByIdTestCase(CourseTestCase):
"url"
:
"/a/b/c/ch1.pdf"
,
"url"
:
"/a/b/c/ch1.pdf"
,
}
}
}
}
self
.
url1
=
reverse
(
'textbook_by_id'
,
kwargs
=
{
self
.
url1
=
self
.
course_locator
.
url_reverse
(
"textbooks"
,
"1"
)
'org'
:
self
.
course
.
location
.
org
,
'course'
:
self
.
course
.
location
.
course
,
'name'
:
self
.
course
.
location
.
name
,
'tid'
:
1
,
})
self
.
textbook2
=
{
self
.
textbook2
=
{
"tab_title"
:
"Algebra"
,
"tab_title"
:
"Algebra"
,
"id"
:
2
,
"id"
:
2
,
...
@@ -216,24 +194,14 @@ class TextbookByIdTestCase(CourseTestCase):
...
@@ -216,24 +194,14 @@ class TextbookByIdTestCase(CourseTestCase):
"url"
:
"/a/b/ch11.pdf"
,
"url"
:
"/a/b/ch11.pdf"
,
}
}
}
}
self
.
url2
=
reverse
(
'textbook_by_id'
,
kwargs
=
{
self
.
url2
=
self
.
course_locator
.
url_reverse
(
"textbooks"
,
"2"
)
'org'
:
self
.
course
.
location
.
org
,
'course'
:
self
.
course
.
location
.
course
,
'name'
:
self
.
course
.
location
.
name
,
'tid'
:
2
,
})
self
.
course
.
pdf_textbooks
=
[
self
.
textbook1
,
self
.
textbook2
]
self
.
course
.
pdf_textbooks
=
[
self
.
textbook1
,
self
.
textbook2
]
# Save the data that we've just changed to the underlying
# Save the data that we've just changed to the underlying
# MongoKeyValueStore before we update the mongo datastore.
# MongoKeyValueStore before we update the mongo datastore.
self
.
course
.
save
()
self
.
course
.
save
()
self
.
store
=
get_modulestore
(
self
.
course
.
location
)
self
.
store
=
get_modulestore
(
self
.
course
.
location
)
self
.
store
.
update_metadata
(
self
.
course
.
location
,
own_metadata
(
self
.
course
))
self
.
store
.
update_metadata
(
self
.
course
.
location
,
own_metadata
(
self
.
course
))
self
.
url_nonexist
=
reverse
(
'textbook_by_id'
,
kwargs
=
{
self
.
url_nonexist
=
self
.
course_locator
.
url_reverse
(
"textbooks"
,
"20"
)
'org'
:
self
.
course
.
location
.
org
,
'course'
:
self
.
course
.
location
.
course
,
'name'
:
self
.
course
.
location
.
name
,
'tid'
:
20
,
})
def
test_get_1
(
self
):
def
test_get_1
(
self
):
"Get the first textbook"
"Get the first textbook"
...
@@ -275,12 +243,7 @@ class TextbookByIdTestCase(CourseTestCase):
...
@@ -275,12 +243,7 @@ class TextbookByIdTestCase(CourseTestCase):
"url"
:
"supercool.pdf"
,
"url"
:
"supercool.pdf"
,
"id"
:
"1supercool"
,
"id"
:
"1supercool"
,
}
}
url
=
reverse
(
"textbook_by_id"
,
kwargs
=
{
url
=
self
.
course_locator
.
url_reverse
(
"textbooks"
,
"1supercool"
)
'org'
:
self
.
course
.
location
.
org
,
'course'
:
self
.
course
.
location
.
course
,
'name'
:
self
.
course
.
location
.
name
,
'tid'
:
"1supercool"
,
})
resp
=
self
.
client
.
post
(
resp
=
self
.
client
.
post
(
url
,
url
,
data
=
json
.
dumps
(
textbook
),
data
=
json
.
dumps
(
textbook
),
...
...
cms/djangoapps/contentstore/tests/utils.py
View file @
0d349037
...
@@ -57,6 +57,7 @@ class AjaxEnabledTestClient(Client):
...
@@ -57,6 +57,7 @@ class AjaxEnabledTestClient(Client):
"""
"""
return
self
.
get
(
path
,
data
or
{},
follow
,
HTTP_ACCEPT
=
"application/json"
,
**
extra
)
return
self
.
get
(
path
,
data
or
{},
follow
,
HTTP_ACCEPT
=
"application/json"
,
**
extra
)
@override_settings
(
MODULESTORE
=
TEST_MODULESTORE
)
@override_settings
(
MODULESTORE
=
TEST_MODULESTORE
)
class
CourseTestCase
(
ModuleStoreTestCase
):
class
CourseTestCase
(
ModuleStoreTestCase
):
def
setUp
(
self
):
def
setUp
(
self
):
...
@@ -111,7 +112,7 @@ class CourseTestCase(ModuleStoreTestCase):
...
@@ -111,7 +112,7 @@ class CourseTestCase(ModuleStoreTestCase):
client
=
Client
()
client
=
Client
()
client
.
login
(
username
=
uname
,
password
=
password
)
client
.
login
(
username
=
uname
,
password
=
password
)
return
client
,
nonstaff
return
client
,
nonstaff
def
populateCourse
(
self
):
def
populateCourse
(
self
):
"""
"""
Add 2 chapters, 4 sections, 8 verticals, 16 problems to self.course (branching 2)
Add 2 chapters, 4 sections, 8 verticals, 16 problems to self.course (branching 2)
...
...
cms/djangoapps/contentstore/views/course.py
View file @
0d349037
...
@@ -38,7 +38,7 @@ from models.settings.course_metadata import CourseMetadata
...
@@ -38,7 +38,7 @@ from models.settings.course_metadata import CourseMetadata
from
auth.authz
import
create_all_course_groups
,
is_user_in_creator_group
from
auth.authz
import
create_all_course_groups
,
is_user_in_creator_group
from
util.json_request
import
expect_json
from
util.json_request
import
expect_json
from
.access
import
has_access
,
get_location_and_verify_access
from
.access
import
has_access
from
.tabs
import
initialize_course_tabs
from
.tabs
import
initialize_course_tabs
from
.component
import
(
from
.component
import
(
OPEN_ENDED_COMPONENT_TYPES
,
NOTE_COMPONENT_TYPES
,
OPEN_ENDED_COMPONENT_TYPES
,
NOTE_COMPONENT_TYPES
,
...
@@ -57,8 +57,20 @@ __all__ = ['course_info_handler', 'course_handler', 'course_info_update_handler'
...
@@ -57,8 +57,20 @@ __all__ = ['course_info_handler', 'course_handler', 'course_info_update_handler'
'settings_handler'
,
'settings_handler'
,
'grading_handler'
,
'grading_handler'
,
'advanced_settings_handler'
,
'advanced_settings_handler'
,
'textbook_index'
,
'textbook_by_id'
,
'textbooks_list_handler'
,
'textbooks_detail_handler'
]
'create_textbook'
]
def
_get_locator_and_course
(
course_id
,
branch
,
version_guid
,
usage_id
,
user
,
depth
=
0
):
"""
Internal method used to calculate and return the locator and course module
for the view functions in this file.
"""
locator
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
usage_id
=
usage_id
)
if
not
has_access
(
user
,
locator
):
raise
PermissionDenied
()
course_location
=
loc_mapper
()
.
translate_locator_to_location
(
locator
)
course_module
=
modulestore
()
.
get_item
(
course_location
,
depth
=
depth
)
return
locator
,
course_module
# pylint: disable=unused-argument
# pylint: disable=unused-argument
...
@@ -168,17 +180,10 @@ def course_index(request, course_id, branch, version_guid, block):
...
@@ -168,17 +180,10 @@ def course_index(request, course_id, branch, version_guid, block):
org, course, name: Attributes of the Location for the item to edit
org, course, name: Attributes of the Location for the item to edit
"""
"""
location
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
usage_id
=
block
)
locator
,
course
=
_get_locator_and_course
(
# TODO: when converting to split backend, if location does not have a usage_id,
course_id
,
branch
,
version_guid
,
block
,
request
.
user
,
depth
=
3
# we'll need to get the course's root block_id
)
if
not
has_access
(
request
.
user
,
location
):
lms_link
=
get_lms_link_for_item
(
course
.
location
)
raise
PermissionDenied
()
old_location
=
loc_mapper
()
.
translate_locator_to_location
(
location
)
lms_link
=
get_lms_link_for_item
(
old_location
)
course
=
modulestore
()
.
get_item
(
old_location
,
depth
=
3
)
sections
=
course
.
get_children
()
sections
=
course
.
get_children
()
return
render_to_response
(
'overview.html'
,
{
return
render_to_response
(
'overview.html'
,
{
...
@@ -186,9 +191,9 @@ def course_index(request, course_id, branch, version_guid, block):
...
@@ -186,9 +191,9 @@ def course_index(request, course_id, branch, version_guid, block):
'lms_link'
:
lms_link
,
'lms_link'
:
lms_link
,
'sections'
:
sections
,
'sections'
:
sections
,
'course_graders'
:
json
.
dumps
(
'course_graders'
:
json
.
dumps
(
CourseGradingModel
.
fetch
(
locat
ion
)
.
graders
CourseGradingModel
.
fetch
(
locat
or
)
.
graders
),
),
'parent_locator'
:
locat
ion
,
'parent_locator'
:
locat
or
,
'new_section_category'
:
'chapter'
,
'new_section_category'
:
'chapter'
,
'new_subsection_category'
:
'sequential'
,
'new_subsection_category'
:
'sequential'
,
'new_unit_category'
:
'vertical'
,
'new_unit_category'
:
'vertical'
,
...
@@ -314,22 +319,18 @@ def course_info_handler(request, tag=None, course_id=None, branch=None, version_
...
@@ -314,22 +319,18 @@ def course_info_handler(request, tag=None, course_id=None, branch=None, version_
GET
GET
html: return html for editing the course info handouts and updates.
html: return html for editing the course info handouts and updates.
"""
"""
course_location
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
usage_id
=
block
)
__
,
course_module
=
_get_locator_and_course
(
course_old_location
=
loc_mapper
()
.
translate_locator_to_location
(
course_location
)
course_id
,
branch
,
version_guid
,
block
,
request
.
user
)
if
'text/html'
in
request
.
META
.
get
(
'HTTP_ACCEPT'
,
'text/html'
):
if
'text/html'
in
request
.
META
.
get
(
'HTTP_ACCEPT'
,
'text/html'
):
if
not
has_access
(
request
.
user
,
course_location
):
handouts_old_location
=
course_module
.
location
.
replace
(
category
=
'course_info'
,
name
=
'handouts'
)
raise
PermissionDenied
()
course_module
=
modulestore
()
.
get_item
(
course_old_location
)
handouts_old_location
=
course_old_location
.
replace
(
category
=
'course_info'
,
name
=
'handouts'
)
handouts_locator
=
loc_mapper
()
.
translate_location
(
handouts_locator
=
loc_mapper
()
.
translate_location
(
course_
old_
location
.
course_id
,
handouts_old_location
,
False
,
True
course_
module
.
location
.
course_id
,
handouts_old_location
,
False
,
True
)
)
update_location
=
course_
old_
location
.
replace
(
category
=
'course_info'
,
name
=
'updates'
)
update_location
=
course_
module
.
location
.
replace
(
category
=
'course_info'
,
name
=
'updates'
)
update_locator
=
loc_mapper
()
.
translate_location
(
update_locator
=
loc_mapper
()
.
translate_location
(
course_
old_
location
.
course_id
,
update_location
,
False
,
True
course_
module
.
location
.
course_id
,
update_location
,
False
,
True
)
)
return
render_to_response
(
return
render_to_response
(
...
@@ -338,7 +339,7 @@ def course_info_handler(request, tag=None, course_id=None, branch=None, version_
...
@@ -338,7 +339,7 @@ def course_info_handler(request, tag=None, course_id=None, branch=None, version_
'context_course'
:
course_module
,
'context_course'
:
course_module
,
'updates_url'
:
update_locator
.
url_reverse
(
'course_info_update/'
),
'updates_url'
:
update_locator
.
url_reverse
(
'course_info_update/'
),
'handouts_locator'
:
handouts_locator
,
'handouts_locator'
:
handouts_locator
,
'base_asset_url'
:
StaticContent
.
get_base_url_path_for_course_assets
(
course_
old_
location
)
+
'/'
'base_asset_url'
:
StaticContent
.
get_base_url_path_for_course_assets
(
course_
module
.
location
)
+
'/'
}
}
)
)
else
:
else
:
...
@@ -407,20 +408,16 @@ def settings_handler(request, tag=None, course_id=None, branch=None, version_gui
...
@@ -407,20 +408,16 @@ def settings_handler(request, tag=None, course_id=None, branch=None, version_gui
PUT
PUT
json: update the Course and About xblocks through the CourseDetails model
json: update the Course and About xblocks through the CourseDetails model
"""
"""
locator
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
usage_id
=
block
)
locator
,
course_module
=
_get_locator_and_course
(
if
not
has_access
(
request
.
user
,
locator
):
course_id
,
branch
,
version_guid
,
block
,
request
.
user
raise
PermissionDenied
()
)
if
'text/html'
in
request
.
META
.
get
(
'HTTP_ACCEPT'
,
''
)
and
request
.
method
==
'GET'
:
if
'text/html'
in
request
.
META
.
get
(
'HTTP_ACCEPT'
,
''
)
and
request
.
method
==
'GET'
:
course_old_location
=
loc_mapper
()
.
translate_locator_to_location
(
locator
)
course_module
=
modulestore
()
.
get_item
(
course_old_location
)
upload_asset_url
=
locator
.
url_reverse
(
'assets/'
)
upload_asset_url
=
locator
.
url_reverse
(
'assets/'
)
return
render_to_response
(
'settings.html'
,
{
return
render_to_response
(
'settings.html'
,
{
'context_course'
:
course_module
,
'context_course'
:
course_module
,
'course_locator'
:
locator
,
'course_locator'
:
locator
,
'lms_link_for_about_page'
:
utils
.
get_lms_link_for_about_page
(
course_
old_
location
),
'lms_link_for_about_page'
:
utils
.
get_lms_link_for_about_page
(
course_
module
.
location
),
'course_image_url'
:
utils
.
course_image_url
(
course_module
),
'course_image_url'
:
utils
.
course_image_url
(
course_module
),
'details_url'
:
locator
.
url_reverse
(
'/settings/details/'
),
'details_url'
:
locator
.
url_reverse
(
'/settings/details/'
),
'about_page_editable'
:
not
settings
.
FEATURES
.
get
(
'about_page_editable'
:
not
settings
.
FEATURES
.
get
(
...
@@ -457,13 +454,11 @@ def grading_handler(request, tag=None, course_id=None, branch=None, version_guid
...
@@ -457,13 +454,11 @@ def grading_handler(request, tag=None, course_id=None, branch=None, version_guid
json no grader_index: update the Course through the CourseGrading model
json no grader_index: update the Course through the CourseGrading model
json w/ grader_index: create or update the specific grader (create if index out of range)
json w/ grader_index: create or update the specific grader (create if index out of range)
"""
"""
locator
=
BlockUsageLocator
(
course_id
=
course_id
,
branch
=
branch
,
version_guid
=
version_guid
,
usage_id
=
block
)
locator
,
course_module
=
_get_locator_and_course
(
if
not
has_access
(
request
.
user
,
locator
):
course_id
,
branch
,
version_guid
,
block
,
request
.
user
raise
PermissionDenied
(
)
)
if
'text/html'
in
request
.
META
.
get
(
'HTTP_ACCEPT'
,
''
)
and
request
.
method
==
'GET'
:
if
'text/html'
in
request
.
META
.
get
(
'HTTP_ACCEPT'
,
''
)
and
request
.
method
==
'GET'
:
course_old_location
=
loc_mapper
()
.
translate_locator_to_location
(
locator
)
course_module
=
modulestore
()
.
get_item
(
course_old_location
)
course_details
=
CourseGradingModel
.
fetch
(
locator
)
course_details
=
CourseGradingModel
.
fetch
(
locator
)
return
render_to_response
(
'settings_graders.html'
,
{
return
render_to_response
(
'settings_graders.html'
,
{
...
@@ -514,8 +509,8 @@ def _config_course_advanced_components(request, course_module):
...
@@ -514,8 +509,8 @@ def _config_course_advanced_components(request, course_module):
filter_tabs
=
True
# Exceptional conditions will pull this to False
filter_tabs
=
True
# Exceptional conditions will pull this to False
if
ADVANCED_COMPONENT_POLICY_KEY
in
request
.
json
:
# Maps tab types to components
if
ADVANCED_COMPONENT_POLICY_KEY
in
request
.
json
:
# 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
...
@@ -565,13 +560,9 @@ def advanced_settings_handler(request, course_id=None, branch=None, version_guid
...
@@ -565,13 +560,9 @@ def advanced_settings_handler(request, course_id=None, branch=None, version_guid
metadata dicts. The dict can include a "unsetKeys" entry which is a list
metadata dicts. The dict can include a "unsetKeys" entry which is a list
of keys whose values to unset: i.e., revert to default
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
)
locator
,
course_module
=
_get_locator_and_course
(
if
not
has_access
(
request
.
user
,
locator
):
course_id
,
branch
,
version_guid
,
block
,
request
.
user
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'
:
if
'text/html'
in
request
.
META
.
get
(
'HTTP_ACCEPT'
,
''
)
and
request
.
method
==
'GET'
:
return
render_to_response
(
'settings_advanced.html'
,
{
return
render_to_response
(
'settings_advanced.html'
,
{
...
@@ -657,113 +648,109 @@ def assign_textbook_id(textbook, used_ids=()):
...
@@ -657,113 +648,109 @@ def assign_textbook_id(textbook, used_ids=()):
return
tid
return
tid
@require_http_methods
((
"GET"
,
"POST"
,
"PUT"
))
@login_required
@login_required
@ensure_csrf_cookie
@ensure_csrf_cookie
def
textbook
_index
(
request
,
org
,
course
,
nam
e
):
def
textbook
s_list_handler
(
request
,
tag
=
None
,
course_id
=
None
,
branch
=
None
,
version_guid
=
None
,
block
=
Non
e
):
"""
"""
Display an editable textbook overview
.
A RESTful handler for textbook collections
.
org, course, name: Attributes of the Location for the item to edit
GET
html: return textbook list page (Backbone application)
json: return JSON representation of all textbooks in this course
POST
json: create a new textbook for this course
PUT
json: overwrite all textbooks in the course with the given list
"""
"""
location
=
get_location_and_verify_access
(
request
,
org
,
course
,
name
)
locator
,
course
=
_get_locator_and_course
(
store
=
get_modulestore
(
location
)
course_id
,
branch
,
version_guid
,
block
,
request
.
user
course_module
=
store
.
get_item
(
location
,
depth
=
3
)
)
store
=
get_modulestore
(
course
.
location
)
if
request
.
is_ajax
():
if
not
"application/json"
in
request
.
META
.
get
(
'HTTP_ACCEPT'
,
'text/html'
):
if
request
.
method
==
'GET'
:
# return HTML page
return
JsonResponse
(
course_module
.
pdf_textbooks
)
upload_asset_url
=
locator
.
url_reverse
(
'assets/'
,
''
)
# can be either and sometimes django is rewriting one to the other:
textbook_url
=
locator
.
url_reverse
(
'/textbooks'
)
elif
request
.
method
in
(
'POST'
,
'PUT'
):
try
:
textbooks
=
validate_textbooks_json
(
request
.
body
)
except
TextbookValidationError
as
err
:
return
JsonResponse
({
"error"
:
err
.
message
},
status
=
400
)
tids
=
set
(
t
[
"id"
]
for
t
in
textbooks
if
"id"
in
t
)
for
textbook
in
textbooks
:
if
not
"id"
in
textbook
:
tid
=
assign_textbook_id
(
textbook
,
tids
)
textbook
[
"id"
]
=
tid
tids
.
add
(
tid
)
if
not
any
(
tab
[
'type'
]
==
'pdf_textbooks'
for
tab
in
course_module
.
tabs
):
course_module
.
tabs
.
append
({
"type"
:
"pdf_textbooks"
})
course_module
.
pdf_textbooks
=
textbooks
# Save the data that we've just changed to the underlying
# MongoKeyValueStore before we update the mongo datastore.
course_module
.
save
()
store
.
update_metadata
(
course_module
.
location
,
own_metadata
(
course_module
)
)
return
JsonResponse
(
course_module
.
pdf_textbooks
)
else
:
new_loc
=
loc_mapper
()
.
translate_location
(
location
.
course_id
,
location
,
False
,
True
)
upload_asset_url
=
new_loc
.
url_reverse
(
'assets/'
,
''
)
textbook_url
=
reverse
(
'textbook_index'
,
kwargs
=
{
'org'
:
org
,
'course'
:
course
,
'name'
:
name
,
})
return
render_to_response
(
'textbooks.html'
,
{
return
render_to_response
(
'textbooks.html'
,
{
'context_course'
:
course
_module
,
'context_course'
:
course
,
'course'
:
course
_module
,
'course'
:
course
,
'upload_asset_url'
:
upload_asset_url
,
'upload_asset_url'
:
upload_asset_url
,
'textbook_url'
:
textbook_url
,
'textbook_url'
:
textbook_url
,
})
})
# from here on down, we know the client has requested JSON
if
request
.
method
==
'GET'
:
return
JsonResponse
(
course
.
pdf_textbooks
)
elif
request
.
method
==
'PUT'
:
try
:
textbooks
=
validate_textbooks_json
(
request
.
body
)
except
TextbookValidationError
as
err
:
return
JsonResponse
({
"error"
:
err
.
message
},
status
=
400
)
@require_POST
tids
=
set
(
t
[
"id"
]
for
t
in
textbooks
if
"id"
in
t
)
@login_required
for
textbook
in
textbooks
:
@ensure_csrf_cookie
if
not
"id"
in
textbook
:
def
create_textbook
(
request
,
org
,
course
,
name
):
tid
=
assign_textbook_id
(
textbook
,
tids
)
"""
textbook
[
"id"
]
=
tid
JSON API endpoint for creating a textbook. Used by the Backbone application.
tids
.
add
(
tid
)
"""
location
=
get_location_and_verify_access
(
request
,
org
,
course
,
name
)
store
=
get_modulestore
(
location
)
course_module
=
store
.
get_item
(
location
,
depth
=
0
)
try
:
if
not
any
(
tab
[
'type'
]
==
'pdf_textbooks'
for
tab
in
course
.
tabs
):
textbook
=
validate_textbook_json
(
request
.
body
)
course
.
tabs
.
append
({
"type"
:
"pdf_textbooks"
})
except
TextbookValidationError
as
err
:
course
.
pdf_textbooks
=
textbooks
return
JsonResponse
({
"error"
:
err
.
message
},
status
=
400
)
# Save the data that we've just changed to the underlying
if
not
textbook
.
get
(
"id"
):
# MongoKeyValueStore before we update the mongo datastore.
tids
=
set
(
t
[
"id"
]
for
t
in
course_module
.
pdf_textbooks
if
"id"
in
t
)
course
.
save
()
textbook
[
"id"
]
=
assign_textbook_id
(
textbook
,
tids
)
store
.
update_metadata
(
existing
=
course_module
.
pdf_textbooks
course
.
location
,
existing
.
append
(
textbook
)
own_metadata
(
course
)
course_module
.
pdf_textbooks
=
existing
)
if
not
any
(
tab
[
'type'
]
==
'pdf_textbooks'
for
tab
in
course_module
.
tabs
):
return
JsonResponse
(
course
.
pdf_textbooks
)
tabs
=
course_module
.
tabs
elif
request
.
method
==
'POST'
:
tabs
.
append
({
"type"
:
"pdf_textbooks"
})
# create a new textbook for the course
course_module
.
tabs
=
tabs
try
:
# Save the data that we've just changed to the underlying
textbook
=
validate_textbook_json
(
request
.
body
)
# MongoKeyValueStore before we update the mongo datastore.
except
TextbookValidationError
as
err
:
course_module
.
save
()
return
JsonResponse
({
"error"
:
err
.
message
},
status
=
400
)
store
.
update_metadata
(
course_module
.
location
,
own_metadata
(
course_module
))
if
not
textbook
.
get
(
"id"
):
resp
=
JsonResponse
(
textbook
,
status
=
201
)
tids
=
set
(
t
[
"id"
]
for
t
in
course
.
pdf_textbooks
if
"id"
in
t
)
resp
[
"Location"
]
=
reverse
(
"textbook_by_id"
,
kwargs
=
{
textbook
[
"id"
]
=
assign_textbook_id
(
textbook
,
tids
)
'org'
:
org
,
existing
=
course
.
pdf_textbooks
'course'
:
course
,
existing
.
append
(
textbook
)
'name'
:
name
,
course
.
pdf_textbooks
=
existing
'tid'
:
textbook
[
"id"
],
if
not
any
(
tab
[
'type'
]
==
'pdf_textbooks'
for
tab
in
course
.
tabs
):
})
tabs
=
course
.
tabs
return
resp
tabs
.
append
({
"type"
:
"pdf_textbooks"
})
course
.
tabs
=
tabs
# Save the data that we've just changed to the underlying
# MongoKeyValueStore before we update the mongo datastore.
course
.
save
()
store
.
update_metadata
(
course
.
location
,
own_metadata
(
course
))
resp
=
JsonResponse
(
textbook
,
status
=
201
)
resp
[
"Location"
]
=
locator
.
url_reverse
(
'textbooks'
,
textbook
[
"id"
])
return
resp
@login_required
@login_required
@ensure_csrf_cookie
@ensure_csrf_cookie
@require_http_methods
((
"GET"
,
"POST"
,
"PUT"
,
"DELETE"
))
@require_http_methods
((
"GET"
,
"POST"
,
"PUT"
,
"DELETE"
))
def
textbook
_by_id
(
request
,
org
,
course
,
name
,
tid
):
def
textbook
s_detail_handler
(
request
,
tid
,
tag
=
None
,
course_id
=
None
,
branch
=
None
,
version_guid
=
None
,
block
=
None
):
"""
"""
JSON API endpoint for manipulating a textbook via its internal ID.
JSON API endpoint for manipulating a textbook via its internal ID.
Used by the Backbone application.
Used by the Backbone application.
GET
json: return JSON representation of textbook
POST or PUT
json: update textbook based on provided information
DELETE
json: remove textbook
"""
"""
location
=
get_location_and_verify_access
(
request
,
org
,
course
,
name
)
__
,
course
=
_get_locator_and_course
(
store
=
get_modulestore
(
location
)
course_id
,
branch
,
version_guid
,
block
,
request
.
user
course_module
=
store
.
get_item
(
location
,
depth
=
3
)
)
matching_id
=
[
tb
for
tb
in
course_module
.
pdf_textbooks
store
=
get_modulestore
(
course
.
location
)
matching_id
=
[
tb
for
tb
in
course
.
pdf_textbooks
if
str
(
tb
.
get
(
"id"
))
==
str
(
tid
)]
if
str
(
tb
.
get
(
"id"
))
==
str
(
tid
)]
if
matching_id
:
if
matching_id
:
textbook
=
matching_id
[
0
]
textbook
=
matching_id
[
0
]
...
@@ -782,32 +769,32 @@ def textbook_by_id(request, org, course, name, tid):
...
@@ -782,32 +769,32 @@ def textbook_by_id(request, org, course, name, tid):
return
JsonResponse
({
"error"
:
err
.
message
},
status
=
400
)
return
JsonResponse
({
"error"
:
err
.
message
},
status
=
400
)
new_textbook
[
"id"
]
=
tid
new_textbook
[
"id"
]
=
tid
if
textbook
:
if
textbook
:
i
=
course
_module
.
pdf_textbooks
.
index
(
textbook
)
i
=
course
.
pdf_textbooks
.
index
(
textbook
)
new_textbooks
=
course
_module
.
pdf_textbooks
[
0
:
i
]
new_textbooks
=
course
.
pdf_textbooks
[
0
:
i
]
new_textbooks
.
append
(
new_textbook
)
new_textbooks
.
append
(
new_textbook
)
new_textbooks
.
extend
(
course
_module
.
pdf_textbooks
[
i
+
1
:])
new_textbooks
.
extend
(
course
.
pdf_textbooks
[
i
+
1
:])
course
_module
.
pdf_textbooks
=
new_textbooks
course
.
pdf_textbooks
=
new_textbooks
else
:
else
:
course
_module
.
pdf_textbooks
.
append
(
new_textbook
)
course
.
pdf_textbooks
.
append
(
new_textbook
)
# Save the data that we've just changed to the underlying
# Save the data that we've just changed to the underlying
# MongoKeyValueStore before we update the mongo datastore.
# MongoKeyValueStore before we update the mongo datastore.
course
_module
.
save
()
course
.
save
()
store
.
update_metadata
(
store
.
update_metadata
(
course
_module
.
location
,
course
.
location
,
own_metadata
(
course
_module
)
own_metadata
(
course
)
)
)
return
JsonResponse
(
new_textbook
,
status
=
201
)
return
JsonResponse
(
new_textbook
,
status
=
201
)
elif
request
.
method
==
'DELETE'
:
elif
request
.
method
==
'DELETE'
:
if
not
textbook
:
if
not
textbook
:
return
JsonResponse
(
status
=
404
)
return
JsonResponse
(
status
=
404
)
i
=
course
_module
.
pdf_textbooks
.
index
(
textbook
)
i
=
course
.
pdf_textbooks
.
index
(
textbook
)
new_textbooks
=
course
_module
.
pdf_textbooks
[
0
:
i
]
new_textbooks
=
course
.
pdf_textbooks
[
0
:
i
]
new_textbooks
.
extend
(
course
_module
.
pdf_textbooks
[
i
+
1
:])
new_textbooks
.
extend
(
course
.
pdf_textbooks
[
i
+
1
:])
course
_module
.
pdf_textbooks
=
new_textbooks
course
.
pdf_textbooks
=
new_textbooks
course
_module
.
save
()
course
.
save
()
store
.
update_metadata
(
store
.
update_metadata
(
course
_module
.
location
,
course
.
location
,
own_metadata
(
course
_module
)
own_metadata
(
course
)
)
)
return
JsonResponse
()
return
JsonResponse
()
...
...
cms/static/coffee/spec/main_spec.coffee
View file @
0d349037
require
[
"jquery"
,
"backbone"
,
"coffee/src/main"
,
"sinon"
,
"jasmine-stealth"
],
require
[
"jquery"
,
"backbone"
,
"coffee/src/main"
,
"sinon"
,
"jasmine-stealth"
,
"jquery.cookie"
],
(
$
,
Backbone
,
main
,
sinon
)
->
(
$
,
Backbone
,
main
,
sinon
)
->
describe
"CMS"
,
->
describe
"CMS"
,
->
it
"should initialize URL"
,
->
it
"should initialize URL"
,
->
...
...
cms/static/coffee/spec/models/textbook_spec.coffee
View file @
0d349037
...
@@ -11,6 +11,10 @@ define ["backbone", "js/models/textbook", "js/collections/textbook", "js/models/
...
@@ -11,6 +11,10 @@ define ["backbone", "js/models/textbook", "js/collections/textbook", "js/models/
beforeEach
->
beforeEach
->
main
()
main
()
@
model
=
new
Textbook
()
@
model
=
new
Textbook
()
CMS
.
URL
.
TEXTBOOKS
=
"/textbooks"
afterEach
->
delete
CMS
.
URL
.
TEXTBOOKS
describe
"Basic"
,
->
describe
"Basic"
,
->
it
"should have an empty name by default"
,
->
it
"should have an empty name by default"
,
->
...
@@ -28,8 +32,9 @@ define ["backbone", "js/models/textbook", "js/collections/textbook", "js/models/
...
@@ -28,8 +32,9 @@ define ["backbone", "js/models/textbook", "js/collections/textbook", "js/models/
it
"should be empty by default"
,
->
it
"should be empty by default"
,
->
expect
(
@
model
.
isEmpty
()).
toBeTruthy
()
expect
(
@
model
.
isEmpty
()).
toBeTruthy
()
it
"should have a URL set"
,
->
it
"should have a URL root"
,
->
expect
(
@
model
.
url
()).
toBeTruthy
()
urlRoot
=
_
.
result
(
@
model
,
'urlRoot'
)
expect
(
urlRoot
).
toBeTruthy
()
it
"should be able to reset itself"
,
->
it
"should be able to reset itself"
,
->
@
model
.
set
(
"name"
,
"foobar"
)
@
model
.
set
(
"name"
,
"foobar"
)
...
@@ -135,12 +140,8 @@ define ["backbone", "js/models/textbook", "js/collections/textbook", "js/models/
...
@@ -135,12 +140,8 @@ define ["backbone", "js/models/textbook", "js/collections/textbook", "js/models/
delete
CMS
.
URL
.
TEXTBOOKS
delete
CMS
.
URL
.
TEXTBOOKS
it
"should have a url set"
,
->
it
"should have a url set"
,
->
expect
(
@
collection
.
url
()).
toEqual
(
"/textbooks"
)
url
=
_
.
result
(
@
collection
,
'url'
)
expect
(
url
).
toEqual
(
"/textbooks"
)
it
"can call save"
,
->
spyOn
(
@
collection
,
"sync"
)
@
collection
.
save
()
expect
(
@
collection
.
sync
).
toHaveBeenCalledWith
(
"update"
,
@
collection
,
undefined
)
describe
"Chapter model"
,
->
describe
"Chapter model"
,
->
...
...
cms/static/coffee/spec/views/textbook_spec.coffee
View file @
0d349037
...
@@ -81,9 +81,11 @@ define ["js/models/textbook", "js/models/chapter", "js/collections/chapter", "js
...
@@ -81,9 +81,11 @@ define ["js/models/textbook", "js/models/chapter", "js/collections/chapter", "js
@
savingSpies
=
spyOnConstructor
(
Notification
,
"Mini"
,
@
savingSpies
=
spyOnConstructor
(
Notification
,
"Mini"
,
[
"show"
,
"hide"
])
[
"show"
,
"hide"
])
@
savingSpies
.
show
.
andReturn
(
@
savingSpies
)
@
savingSpies
.
show
.
andReturn
(
@
savingSpies
)
CMS
.
URL
.
TEXTBOOKS
=
"/textbooks"
afterEach
->
afterEach
->
@
xhr
.
restore
()
@
xhr
.
restore
()
delete
CMS
.
URL
.
TEXTBOOKS
it
"should destroy itself on confirmation"
,
->
it
"should destroy itself on confirmation"
,
->
@
view
.
render
().
$
(
".delete"
).
click
()
@
view
.
render
().
$
(
".delete"
).
click
()
...
...
cms/static/js/collections/textbook.js
View file @
0d349037
...
@@ -2,10 +2,7 @@ define(["backbone", "js/models/textbook"],
...
@@ -2,10 +2,7 @@ define(["backbone", "js/models/textbook"],
function
(
Backbone
,
TextbookModel
)
{
function
(
Backbone
,
TextbookModel
)
{
var
TextbookCollection
=
Backbone
.
Collection
.
extend
({
var
TextbookCollection
=
Backbone
.
Collection
.
extend
({
model
:
TextbookModel
,
model
:
TextbookModel
,
url
:
function
()
{
return
CMS
.
URL
.
TEXTBOOKS
;
},
url
:
function
()
{
return
CMS
.
URL
.
TEXTBOOKS
;
}
save
:
function
(
options
)
{
return
this
.
sync
(
'update'
,
this
,
options
);
}
});
});
return
TextbookCollection
;
return
TextbookCollection
;
});
});
cms/static/js/models/textbook.js
View file @
0d349037
define
([
"backbone"
,
"underscore"
,
"js/models/chapter"
,
"js/collections/chapter"
,
"backbone.associations"
],
define
([
"backbone"
,
"underscore"
,
"js/models/chapter"
,
"js/collections/chapter"
,
"backbone.associations"
,
"coffee/src/main"
],
function
(
Backbone
,
_
,
ChapterModel
,
ChapterCollection
)
{
function
(
Backbone
,
_
,
ChapterModel
,
ChapterCollection
)
{
var
Textbook
=
Backbone
.
AssociatedModel
.
extend
({
var
Textbook
=
Backbone
.
AssociatedModel
.
extend
({
...
@@ -32,13 +33,7 @@ define(["backbone", "underscore", "js/models/chapter", "js/collections/chapter",
...
@@ -32,13 +33,7 @@ define(["backbone", "underscore", "js/models/chapter", "js/collections/chapter",
isEmpty
:
function
()
{
isEmpty
:
function
()
{
return
!
this
.
get
(
'name'
)
&&
this
.
get
(
'chapters'
).
isEmpty
();
return
!
this
.
get
(
'name'
)
&&
this
.
get
(
'chapters'
).
isEmpty
();
},
},
url
:
function
()
{
urlRoot
:
function
()
{
return
CMS
.
URL
.
TEXTBOOKS
;
},
if
(
this
.
isNew
())
{
return
CMS
.
URL
.
TEXTBOOKS
+
"/new"
;
}
else
{
return
CMS
.
URL
.
TEXTBOOKS
+
"/"
+
this
.
id
;
}
},
parse
:
function
(
response
)
{
parse
:
function
(
response
)
{
var
ret
=
$
.
extend
(
true
,
{},
response
);
var
ret
=
$
.
extend
(
true
,
{},
response
);
if
(
"tab_title"
in
ret
&&
!
(
"name"
in
ret
))
{
if
(
"tab_title"
in
ret
&&
!
(
"name"
in
ret
))
{
...
...
cms/templates/widgets/header.html
View file @
0d349037
...
@@ -13,13 +13,14 @@
...
@@ -13,13 +13,14 @@
<h1
class=
"branding"
><a
href=
"/"
><img
src=
"${static.url("
img
/
logo-edx-studio
.
png
")}"
alt=
"edX Studio"
/></a></h1>
<h1
class=
"branding"
><a
href=
"/"
><img
src=
"${static.url("
img
/
logo-edx-studio
.
png
")}"
alt=
"edX Studio"
/></a></h1>
% if context_course:
% if context_course:
<
%
<
%
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
)
index_url =
location.url_reverse('course')
index_url =
location.url_reverse('course')
checklists_url =
location.url_reverse('checklists')
checklists_url =
location.url_reverse('checklists')
course_team_url =
location.url_reverse('course_team')
course_team_url =
location.url_reverse('course_team')
assets_url =
location.url_reverse('assets')
assets_url =
location.url_reverse('assets')
textbooks_url =
location.url_reverse('textbooks')
import_url =
location.url_reverse('import')
import_url =
location.url_reverse('import')
course_info_url =
location.url_reverse('course_info')
course_info_url =
location.url_reverse('course_info')
export_url =
location.url_reverse('export')
export_url =
location.url_reverse('export')
...
@@ -58,7 +59,7 @@
...
@@ -58,7 +59,7 @@
<a
href=
"${assets_url}"
>
${_("Files
&
Uploads")}
</a>
<a
href=
"${assets_url}"
>
${_("Files
&
Uploads")}
</a>
</li>
</li>
<li
class=
"nav-item nav-course-courseware-textbooks"
>
<li
class=
"nav-item nav-course-courseware-textbooks"
>
<a
href=
"${
reverse('textbook_index', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, name=ctx_loc.name))
}"
>
${_("Textbooks")}
</a>
<a
href=
"${
textbooks_url
}"
>
${_("Textbooks")}
</a>
</li>
</li>
</ul>
</ul>
</div>
</div>
...
...
cms/urls.py
View file @
0d349037
...
@@ -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'
),
url
(
r'^(?P<org>[^/]+)/(?P<course>[^/]+)/textbooks/(?P<name>[^/]+)$'
,
'contentstore.views.textbook_index'
,
name
=
'textbook_index'
),
url
(
r'^(?P<org>[^/]+)/(?P<course>[^/]+)/textbooks/(?P<name>[^/]+)/new$'
,
'contentstore.views.create_textbook'
,
name
=
'create_textbook'
),
url
(
r'^(?P<org>[^/]+)/(?P<course>[^/]+)/textbooks/(?P<name>[^/]+)/(?P<tid>\d[^/]*)$'
,
'contentstore.views.textbook_by_id'
,
name
=
'textbook_by_id'
),
# temporary landing page for a course
# temporary landing page for a course
url
(
r'^edge/(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<coursename>[^/]+)$'
,
url
(
r'^edge/(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<coursename>[^/]+)$'
,
'contentstore.views.landing'
,
name
=
'landing'
),
'contentstore.views.landing'
,
name
=
'landing'
),
...
@@ -89,6 +82,8 @@ urlpatterns += patterns(
...
@@ -89,6 +82,8 @@ urlpatterns += patterns(
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'
),
url
(
r'(?ix)^settings/advanced/{}$'
.
format
(
parsers
.
URL_RE_SOURCE
),
'advanced_settings_handler'
),
url
(
r'(?ix)^textbooks/{}$'
.
format
(
parsers
.
URL_RE_SOURCE
),
'textbooks_list_handler'
),
url
(
r'(?ix)^textbooks/{}/(?P<tid>\d[^/]*)$'
.
format
(
parsers
.
URL_RE_SOURCE
),
'textbooks_detail_handler'
),
)
)
js_info_dict
=
{
js_info_dict
=
{
...
...
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