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
50128cfb
Commit
50128cfb
authored
Nov 20, 2013
by
cahrens
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Convert edit_subsection, edit_unit, and publishing to RESTful URLs.
STUD-844
parent
037cec6c
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
228 additions
and
106 deletions
+228
-106
CHANGELOG.rst
+4
-0
cms/djangoapps/contentstore/tests/test_contentstore.py
+41
-38
cms/djangoapps/contentstore/tests/test_import_export.py
+1
-1
cms/djangoapps/contentstore/tests/test_item.py
+104
-1
cms/djangoapps/contentstore/tests/test_transcripts.py
+10
-4
cms/djangoapps/contentstore/views/component.py
+0
-0
cms/djangoapps/contentstore/views/import_export.py
+9
-10
cms/djangoapps/contentstore/views/item.py
+27
-12
cms/static/coffee/src/views/unit.coffee
+9
-9
cms/static/js/base.js
+1
-1
cms/templates/overview.html
+1
-1
cms/templates/unit.html
+10
-7
cms/templates/widgets/units.html
+1
-1
cms/urls.py
+2
-6
common/lib/xmodule/xmodule/modulestore/loc_mapper_store.py
+4
-9
common/lib/xmodule/xmodule/modulestore/mongo/base.py
+1
-5
common/lib/xmodule/xmodule/modulestore/tests/test_location_mapper.py
+3
-1
No files found.
CHANGELOG.rst
View file @
50128cfb
...
@@ -7,6 +7,10 @@ the top. Include a label indicating the component affected.
...
@@ -7,6 +7,10 @@ the top. Include a label indicating the component affected.
Blades: Enabled several Video Jasmine tests. BLD-463.
Blades: Enabled several Video Jasmine tests. BLD-463.
Studio: Continued modification of Studio pages to follow a RESTful framework.
includes Settings pages, edit page for Subsection and Unit, and interfaces
for updating xblocks (xmodules) and getting their editing HTML.
Blades: Put 2nd "Hide output" button at top of test box & increase text size for
Blades: Put 2nd "Hide output" button at top of test box & increase text size for
code response questions. BLD-126.
code response questions. BLD-126.
...
...
cms/djangoapps/contentstore/tests/test_contentstore.py
View file @
50128cfb
...
@@ -42,6 +42,7 @@ from xmodule.capa_module import CapaDescriptor
...
@@ -42,6 +42,7 @@ from xmodule.capa_module import CapaDescriptor
from
xmodule.course_module
import
CourseDescriptor
from
xmodule.course_module
import
CourseDescriptor
from
xmodule.seq_module
import
SequenceDescriptor
from
xmodule.seq_module
import
SequenceDescriptor
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
xmodule.modulestore.locator
import
BlockUsageLocator
from
contentstore.views.component
import
ADVANCED_COMPONENT_TYPES
from
contentstore.views.component
import
ADVANCED_COMPONENT_TYPES
from
xmodule.exceptions
import
NotFoundError
from
xmodule.exceptions
import
NotFoundError
...
@@ -133,10 +134,10 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
...
@@ -133,10 +134,10 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
# just pick one vertical
# just pick one vertical
descriptor
=
store
.
get_items
(
Location
(
'i4x'
,
'edX'
,
'simple'
,
'vertical'
,
None
,
None
))[
0
]
descriptor
=
store
.
get_items
(
Location
(
'i4x'
,
'edX'
,
'simple'
,
'vertical'
,
None
,
None
))[
0
]
locator
=
loc_mapper
()
.
translate_location
(
course
.
location
.
course_id
,
descriptor
.
location
,
False
,
True
)
resp
=
self
.
client
.
get_html
(
reverse
(
'edit_unit'
,
kwargs
=
{
'location'
:
descriptor
.
location
.
url
()}
))
resp
=
self
.
client
.
get_html
(
locator
.
url_reverse
(
'unit'
))
self
.
assertEqual
(
resp
.
status_code
,
200
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
# TODO: uncomment
after edit_unit no longer using location
s.
# TODO: uncomment
when video transcripts no longer require ID
s.
# _test_no_locations(self, resp)
# _test_no_locations(self, resp)
for
expected
in
expected_types
:
for
expected
in
expected_types
:
...
@@ -155,29 +156,22 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
...
@@ -155,29 +156,22 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
def
test_malformed_edit_unit_request
(
self
):
def
test_malformed_edit_unit_request
(
self
):
store
=
modulestore
(
'direct'
)
store
=
modulestore
(
'direct'
)
import_from_xml
(
store
,
'common/test/data/'
,
[
'simple'
])
_
,
course_items
=
import_from_xml
(
store
,
'common/test/data/'
,
[
'simple'
])
# just pick one vertical
# just pick one vertical
descriptor
=
store
.
get_items
(
Location
(
'i4x'
,
'edX'
,
'simple'
,
'vertical'
,
None
,
None
))[
0
]
descriptor
=
store
.
get_items
(
Location
(
'i4x'
,
'edX'
,
'simple'
,
'vertical'
,
None
,
None
))[
0
]
location
=
descriptor
.
location
.
replace
(
name
=
'.'
+
descriptor
.
location
.
name
)
location
=
descriptor
.
location
.
replace
(
name
=
'.'
+
descriptor
.
location
.
name
)
locator
=
loc_mapper
()
.
translate_location
(
course_items
[
0
]
.
location
.
course_id
,
location
,
False
,
True
)
resp
=
self
.
client
.
get_html
(
reverse
(
'edit_unit'
,
kwargs
=
{
'location'
:
location
.
url
()}
))
resp
=
self
.
client
.
get_html
(
locator
.
url_reverse
(
'unit'
))
self
.
assertEqual
(
resp
.
status_code
,
400
)
self
.
assertEqual
(
resp
.
status_code
,
400
)
_test_no_locations
(
self
,
resp
,
status_code
=
400
)
_test_no_locations
(
self
,
resp
,
status_code
=
400
)
def
check_edit_unit
(
self
,
test_course_name
):
def
check_edit_unit
(
self
,
test_course_name
):
import_from_xml
(
modulestore
(
'direct'
),
'common/test/data/'
,
[
test_course_name
])
_
,
course_items
=
import_from_xml
(
modulestore
(
'direct'
),
'common/test/data/'
,
[
test_course_name
])
items
=
modulestore
()
.
get_items
(
Location
(
'i4x'
,
'edX'
,
test_course_name
,
'vertical'
,
None
,
None
))
items
=
modulestore
()
.
get_items
(
Location
(
'i4x'
,
'edX'
,
test_course_name
,
'vertical'
,
None
,
None
))
# Assert is here to make sure that the course being tested actually has verticals.
self
.
_check_verticals
(
items
,
course_items
[
0
]
.
location
.
course_id
)
self
.
assertGreater
(
len
(
items
),
0
)
for
descriptor
in
items
:
print
"Checking "
,
descriptor
.
location
.
url
()
print
descriptor
.
__class__
,
descriptor
.
location
resp
=
self
.
client
.
get_html
(
reverse
(
'edit_unit'
,
kwargs
=
{
'location'
:
descriptor
.
location
.
url
()}))
self
.
assertEqual
(
resp
.
status_code
,
200
)
# TODO: uncomment after edit_unit not using locations.
# _test_no_locations(self, resp)
def
_lock_an_asset
(
self
,
content_store
,
course_location
):
def
_lock_an_asset
(
self
,
content_store
,
course_location
):
"""
"""
...
@@ -1065,14 +1059,11 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
...
@@ -1065,14 +1059,11 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
target_location_namespace
=
course_location
target_location_namespace
=
course_location
)
)
# Unit test fails in Jenkins without this.
loc_mapper
()
.
translate_location
(
course_location
.
course_id
,
course_location
,
False
,
True
)
items
=
module_store
.
get_items
(
stub_location
.
replace
(
category
=
'vertical'
,
name
=
None
))
items
=
module_store
.
get_items
(
stub_location
.
replace
(
category
=
'vertical'
,
name
=
None
))
self
.
assertGreater
(
len
(
items
),
0
)
self
.
_check_verticals
(
items
,
course_location
.
course_id
)
for
descriptor
in
items
:
print
"Checking {0}...."
.
format
(
descriptor
.
location
.
url
())
resp
=
self
.
client
.
get_html
(
reverse
(
'edit_unit'
,
kwargs
=
{
'location'
:
descriptor
.
location
.
url
()}))
self
.
assertEqual
(
resp
.
status_code
,
200
)
# TODO: uncomment when edit_unit no longer has locations.
# _test_no_locations(self, resp)
# verify that we have the content in the draft store as well
# verify that we have the content in the draft store as well
vertical
=
draft_store
.
get_item
(
vertical
=
draft_store
.
get_item
(
...
@@ -1355,6 +1346,19 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
...
@@ -1355,6 +1346,19 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
items
=
module_store
.
get_items
(
stub_location
)
items
=
module_store
.
get_items
(
stub_location
)
self
.
assertEqual
(
len
(
items
),
1
)
self
.
assertEqual
(
len
(
items
),
1
)
def
_check_verticals
(
self
,
items
,
course_id
):
""" Test getting the editing HTML for each vertical. """
# Assert is here to make sure that the course being tested actually has verticals (units) to check.
self
.
assertGreater
(
len
(
items
),
0
)
for
descriptor
in
items
:
unit_locator
=
loc_mapper
()
.
translate_location
(
course_id
,
descriptor
.
location
,
False
,
True
)
print
"Checking {0}...."
.
format
(
unicode
(
unit_locator
))
print
descriptor
.
__class__
,
descriptor
.
location
resp
=
self
.
client
.
get_html
(
unit_locator
.
url_reverse
(
'unit'
))
self
.
assertEqual
(
resp
.
status_code
,
200
)
# TODO: uncomment when video transcripts no longer require IDs.
# _test_no_locations(self, resp)
@override_settings
(
CONTENTSTORE
=
TEST_DATA_CONTENTSTORE
,
MODULESTORE
=
TEST_MODULESTORE
)
@override_settings
(
CONTENTSTORE
=
TEST_DATA_CONTENTSTORE
,
MODULESTORE
=
TEST_MODULESTORE
)
class
ContentStoreTest
(
ModuleStoreTestCase
):
class
ContentStoreTest
(
ModuleStoreTestCase
):
...
@@ -1598,12 +1602,13 @@ class ContentStoreTest(ModuleStoreTestCase):
...
@@ -1598,12 +1602,13 @@ class ContentStoreTest(ModuleStoreTestCase):
}
}
resp
=
self
.
client
.
ajax_post
(
'/xblock'
,
section_data
)
resp
=
self
.
client
.
ajax_post
(
'/xblock'
,
section_data
)
_test_no_locations
(
self
,
resp
,
html
=
False
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
data
=
parse_json
(
resp
)
data
=
parse_json
(
resp
)
self
.
assertRegexpMatches
(
self
.
assertRegexpMatches
(
data
[
'
id
'
],
data
[
'
locator
'
],
r"^
i4x://MITx/999/chapter/([0-9]|[a-f]){32
}$"
r"^
MITx.999.Robot_Super_Course/branch/draft/block/chapter([0-9]|[a-f]){3
}$"
)
)
def
test_capa_module
(
self
):
def
test_capa_module
(
self
):
...
@@ -1619,7 +1624,7 @@ class ContentStoreTest(ModuleStoreTestCase):
...
@@ -1619,7 +1624,7 @@ class ContentStoreTest(ModuleStoreTestCase):
self
.
assertEqual
(
resp
.
status_code
,
200
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
payload
=
parse_json
(
resp
)
payload
=
parse_json
(
resp
)
problem_loc
=
Location
(
payload
[
'id'
]
)
problem_loc
=
loc_mapper
()
.
translate_locator_to_location
(
BlockUsageLocator
(
payload
[
'locator'
])
)
problem
=
get_modulestore
(
problem_loc
)
.
get_item
(
problem_loc
)
problem
=
get_modulestore
(
problem_loc
)
.
get_item
(
problem_loc
)
# should be a CapaDescriptor
# should be a CapaDescriptor
self
.
assertIsInstance
(
problem
,
CapaDescriptor
,
"New problem is not a CapaDescriptor"
)
self
.
assertIsInstance
(
problem
,
CapaDescriptor
,
"New problem is not a CapaDescriptor"
)
...
@@ -1677,19 +1682,17 @@ class ContentStoreTest(ModuleStoreTestCase):
...
@@ -1677,19 +1682,17 @@ class ContentStoreTest(ModuleStoreTestCase):
# 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'
)
resp
=
self
.
client
.
get_html
(
subsection_locator
=
loc_mapper
()
.
translate_location
(
loc
.
course_id
,
subsection_location
,
False
,
True
)
reverse
(
'edit_subsection'
,
kwargs
=
{
'location'
:
subsection_location
.
url
()})
resp
=
self
.
client
.
get_html
(
subsection_locator
.
url_reverse
(
'subsection'
))
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
# TODO: uncomment when grading and outline not using old locations.
_test_no_locations
(
self
,
resp
)
# _test_no_locations(self, resp)
# go look at the Edit page
# go look at the Edit page
unit_location
=
loc
.
replace
(
category
=
'vertical'
,
name
=
'test_vertical'
)
unit_location
=
loc
.
replace
(
category
=
'vertical'
,
name
=
'test_vertical'
)
resp
=
self
.
client
.
get_html
(
unit_locator
=
loc_mapper
()
.
translate_location
(
loc
.
course_id
,
unit_location
,
False
,
True
)
reverse
(
'edit_unit'
,
kwargs
=
{
'location'
:
unit_location
.
url
()}
))
resp
=
self
.
client
.
get_html
(
unit_locator
.
url_reverse
(
'unit'
))
self
.
assertEqual
(
resp
.
status_code
,
200
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
# TODO: uncomment when
edit_unit not using old location
s.
# TODO: uncomment when
video transcripts no longer require ID
s.
# _test_no_locations(self, resp)
# _test_no_locations(self, resp)
def
delete_item
(
category
,
name
):
def
delete_item
(
category
,
name
):
...
@@ -1899,8 +1902,7 @@ class ContentStoreTest(ModuleStoreTestCase):
...
@@ -1899,8 +1902,7 @@ class ContentStoreTest(ModuleStoreTestCase):
"""
"""
new_location
=
loc_mapper
()
.
translate_location
(
location
.
course_id
,
location
,
False
,
True
)
new_location
=
loc_mapper
()
.
translate_location
(
location
.
course_id
,
location
,
False
,
True
)
resp
=
self
.
client
.
get_html
(
new_location
.
url_reverse
(
'course/'
,
''
))
resp
=
self
.
client
.
get_html
(
new_location
.
url_reverse
(
'course/'
,
''
))
# TODO: uncomment when i4x no longer in overview.
_test_no_locations
(
self
,
resp
)
# _test_no_locations(self, resp)
return
resp
return
resp
...
@@ -2033,7 +2035,8 @@ def _test_no_locations(test, resp, status_code=200, html=True):
...
@@ -2033,7 +2035,8 @@ def _test_no_locations(test, resp, status_code=200, html=True):
# it checks that the HTML properly parses. However, it won't find i4x usages
# it checks that the HTML properly parses. However, it won't find i4x usages
# in JavaScript blocks.
# in JavaScript blocks.
content
=
resp
.
content
content
=
resp
.
content
num_jump_to
=
len
(
re
.
findall
(
r"8000(\S)*jump_to/i4x"
,
content
))
num_jump_to_preview
=
len
(
re
.
findall
(
r"/preview/(\S)*jump_to/i4x"
,
content
))
total_i4x
=
len
(
re
.
findall
(
r"i4x"
,
content
))
num_jump_to_live
=
len
(
re
.
findall
(
r":8000/(\S)*jump_to/i4x"
,
content
))
hits
=
len
(
re
.
findall
(
r"i4x"
,
content
))
-
num_jump_to_preview
-
num_jump_to_live
test
.
assertEqual
(
total_i4x
-
num_jump_to
,
0
,
"i4x found outside of LMS jump-to links"
)
test
.
assertEqual
(
hits
,
0
,
"i4x found outside of LMS jump-to links"
)
cms/djangoapps/contentstore/tests/test_import_export.py
View file @
50128cfb
...
@@ -263,7 +263,7 @@ class ExportTestCase(CourseTestCase):
...
@@ -263,7 +263,7 @@ class ExportTestCase(CourseTestCase):
parent_location
=
vertical
.
location
,
parent_location
=
vertical
.
location
,
category
=
'aawefawef'
category
=
'aawefawef'
)
)
self
.
_verify_export_failure
(
'/edit/i4x://MITx/999/vertical
/foo'
)
self
.
_verify_export_failure
(
u'/unit/MITx.999.Robot_Super_Course/branch/draft/block
/foo'
)
def
_verify_export_failure
(
self
,
expectedText
):
def
_verify_export_failure
(
self
,
expectedText
):
""" Export failure helper method. """
""" Export failure helper method. """
...
...
cms/djangoapps/contentstore/tests/test_item.py
View file @
50128cfb
...
@@ -9,6 +9,7 @@ from xmodule.capa_module import CapaDescriptor
...
@@ -9,6 +9,7 @@ from xmodule.capa_module import CapaDescriptor
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.django
import
loc_mapper
from
xmodule.modulestore.django
import
loc_mapper
from
xmodule.modulestore.locator
import
BlockUsageLocator
from
xmodule.modulestore.locator
import
BlockUsageLocator
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
class
ItemTest
(
CourseTestCase
):
class
ItemTest
(
CourseTestCase
):
...
@@ -30,7 +31,7 @@ class ItemTest(CourseTestCase):
...
@@ -30,7 +31,7 @@ class ItemTest(CourseTestCase):
"""
"""
Get the item referenced by the locator from the modulestore
Get the item referenced by the locator from the modulestore
"""
"""
store
=
modulestore
(
'draft'
)
if
draft
else
modulestore
()
store
=
modulestore
(
'draft'
)
if
draft
else
modulestore
(
'direct'
)
return
store
.
get_item
(
self
.
get_old_id
(
locator
))
return
store
.
get_item
(
self
.
get_old_id
(
locator
))
def
response_locator
(
self
,
response
):
def
response_locator
(
self
,
response
):
...
@@ -251,3 +252,105 @@ class TestEditItem(ItemTest):
...
@@ -251,3 +252,105 @@ class TestEditItem(ItemTest):
self
.
assertEqual
(
self
.
get_old_id
(
self
.
problem_locator
)
.
url
(),
children
[
0
])
self
.
assertEqual
(
self
.
get_old_id
(
self
.
problem_locator
)
.
url
(),
children
[
0
])
self
.
assertEqual
(
self
.
get_old_id
(
unit1_locator
)
.
url
(),
children
[
2
])
self
.
assertEqual
(
self
.
get_old_id
(
unit1_locator
)
.
url
(),
children
[
2
])
self
.
assertEqual
(
self
.
get_old_id
(
unit2_locator
)
.
url
(),
children
[
1
])
self
.
assertEqual
(
self
.
get_old_id
(
unit2_locator
)
.
url
(),
children
[
1
])
def
test_make_public
(
self
):
""" Test making a private problem public (publishing it). """
# When the problem is first created, it is only in draft (because of its category).
with
self
.
assertRaises
(
ItemNotFoundError
):
self
.
get_item_from_modulestore
(
self
.
problem_locator
,
False
)
self
.
client
.
ajax_post
(
self
.
problem_update_url
,
data
=
{
'publish'
:
'make_public'
}
)
self
.
assertIsNotNone
(
self
.
get_item_from_modulestore
(
self
.
problem_locator
,
False
))
def
test_make_private
(
self
):
""" Test making a public problem private (un-publishing it). """
# Make problem public.
self
.
client
.
ajax_post
(
self
.
problem_update_url
,
data
=
{
'publish'
:
'make_public'
}
)
self
.
assertIsNotNone
(
self
.
get_item_from_modulestore
(
self
.
problem_locator
,
False
))
# Now make it private
self
.
client
.
ajax_post
(
self
.
problem_update_url
,
data
=
{
'publish'
:
'make_private'
}
)
with
self
.
assertRaises
(
ItemNotFoundError
):
self
.
get_item_from_modulestore
(
self
.
problem_locator
,
False
)
def
test_make_draft
(
self
):
""" Test creating a draft version of a public problem. """
# Make problem public.
self
.
client
.
ajax_post
(
self
.
problem_update_url
,
data
=
{
'publish'
:
'make_public'
}
)
self
.
assertIsNotNone
(
self
.
get_item_from_modulestore
(
self
.
problem_locator
,
False
))
# Now make it draft, which means both versions will exist.
self
.
client
.
ajax_post
(
self
.
problem_update_url
,
data
=
{
'publish'
:
'create_draft'
}
)
# Update the draft version and check that published is different.
self
.
client
.
ajax_post
(
self
.
problem_update_url
,
data
=
{
'metadata'
:
{
'due'
:
'2077-10-10T04:00Z'
}}
)
published
=
self
.
get_item_from_modulestore
(
self
.
problem_locator
,
False
)
self
.
assertIsNone
(
published
.
due
)
draft
=
self
.
get_item_from_modulestore
(
self
.
problem_locator
,
True
)
self
.
assertEqual
(
draft
.
due
,
datetime
.
datetime
(
2077
,
10
,
10
,
4
,
0
,
tzinfo
=
UTC
))
def
test_make_public_with_update
(
self
):
""" Update a problem and make it public at the same time. """
self
.
client
.
ajax_post
(
self
.
problem_update_url
,
data
=
{
'metadata'
:
{
'due'
:
'2077-10-10T04:00Z'
},
'publish'
:
'make_public'
}
)
published
=
self
.
get_item_from_modulestore
(
self
.
problem_locator
,
False
)
self
.
assertEqual
(
published
.
due
,
datetime
.
datetime
(
2077
,
10
,
10
,
4
,
0
,
tzinfo
=
UTC
))
def
test_make_private_with_update
(
self
):
""" Make a problem private and update it at the same time. """
# Make problem public.
self
.
client
.
ajax_post
(
self
.
problem_update_url
,
data
=
{
'publish'
:
'make_public'
}
)
self
.
client
.
ajax_post
(
self
.
problem_update_url
,
data
=
{
'metadata'
:
{
'due'
:
'2077-10-10T04:00Z'
},
'publish'
:
'make_private'
}
)
with
self
.
assertRaises
(
ItemNotFoundError
):
self
.
get_item_from_modulestore
(
self
.
problem_locator
,
False
)
draft
=
self
.
get_item_from_modulestore
(
self
.
problem_locator
,
True
)
self
.
assertEqual
(
draft
.
due
,
datetime
.
datetime
(
2077
,
10
,
10
,
4
,
0
,
tzinfo
=
UTC
))
def
test_create_draft_with_update
(
self
):
""" Create a draft and update it at the same time. """
# Make problem public.
self
.
client
.
ajax_post
(
self
.
problem_update_url
,
data
=
{
'publish'
:
'make_public'
}
)
self
.
assertIsNotNone
(
self
.
get_item_from_modulestore
(
self
.
problem_locator
,
False
))
# Now make it draft, which means both versions will exist.
self
.
client
.
ajax_post
(
self
.
problem_update_url
,
data
=
{
'metadata'
:
{
'due'
:
'2077-10-10T04:00Z'
},
'publish'
:
'create_draft'
}
)
published
=
self
.
get_item_from_modulestore
(
self
.
problem_locator
,
False
)
self
.
assertIsNone
(
published
.
due
)
draft
=
self
.
get_item_from_modulestore
(
self
.
problem_locator
,
True
)
self
.
assertEqual
(
draft
.
due
,
datetime
.
datetime
(
2077
,
10
,
10
,
4
,
0
,
tzinfo
=
UTC
))
cms/djangoapps/contentstore/tests/test_transcripts.py
View file @
50128cfb
...
@@ -20,6 +20,7 @@ from xmodule.contentstore.django import contentstore, _CONTENTSTORE
...
@@ -20,6 +20,7 @@ from xmodule.contentstore.django import contentstore, _CONTENTSTORE
from
xmodule.contentstore.content
import
StaticContent
from
xmodule.contentstore.content
import
StaticContent
from
xmodule.exceptions
import
NotFoundError
from
xmodule.exceptions
import
NotFoundError
from
xmodule.modulestore.django
import
loc_mapper
from
xmodule.modulestore.django
import
loc_mapper
from
xmodule.modulestore.locator
import
BlockUsageLocator
from
contentstore.tests.modulestore_config
import
TEST_MODULESTORE
from
contentstore.tests.modulestore_config
import
TEST_MODULESTORE
TEST_DATA_CONTENTSTORE
=
copy
.
deepcopy
(
settings
.
CONTENTSTORE
)
TEST_DATA_CONTENTSTORE
=
copy
.
deepcopy
(
settings
.
CONTENTSTORE
)
...
@@ -59,7 +60,7 @@ class Basetranscripts(CourseTestCase):
...
@@ -59,7 +60,7 @@ class Basetranscripts(CourseTestCase):
'type'
:
'video'
'type'
:
'video'
}
}
resp
=
self
.
client
.
ajax_post
(
'/xblock'
,
data
)
resp
=
self
.
client
.
ajax_post
(
'/xblock'
,
data
)
self
.
item_location
=
json
.
loads
(
resp
.
content
)
.
get
(
'id'
)
self
.
item_location
=
self
.
_get_location
(
resp
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
# hI10vDNYz4M - valid Youtube ID with transcripts.
# hI10vDNYz4M - valid Youtube ID with transcripts.
...
@@ -72,6 +73,11 @@ class Basetranscripts(CourseTestCase):
...
@@ -72,6 +73,11 @@ class Basetranscripts(CourseTestCase):
# Remove all transcripts for current module.
# Remove all transcripts for current module.
self
.
clear_subs_content
()
self
.
clear_subs_content
()
def
_get_location
(
self
,
resp
):
""" Returns the location (as a string) from the response returned by a create operation. """
locator
=
json
.
loads
(
resp
.
content
)
.
get
(
'locator'
)
return
loc_mapper
()
.
translate_locator_to_location
(
BlockUsageLocator
(
locator
))
.
url
()
def
get_youtube_ids
(
self
):
def
get_youtube_ids
(
self
):
"""Return youtube speeds and ids."""
"""Return youtube speeds and ids."""
item
=
modulestore
()
.
get_item
(
self
.
item_location
)
item
=
modulestore
()
.
get_item
(
self
.
item_location
)
...
@@ -205,7 +211,7 @@ class TestUploadtranscripts(Basetranscripts):
...
@@ -205,7 +211,7 @@ class TestUploadtranscripts(Basetranscripts):
'type'
:
'non_video'
'type'
:
'non_video'
}
}
resp
=
self
.
client
.
ajax_post
(
'/xblock'
,
data
)
resp
=
self
.
client
.
ajax_post
(
'/xblock'
,
data
)
item_location
=
json
.
loads
(
resp
.
content
)
.
get
(
'id'
)
item_location
=
self
.
_get_location
(
resp
)
data
=
'<non_video youtube="0.75:JMD_ifUUfsU,1.0:hI10vDNYz4M" />'
data
=
'<non_video youtube="0.75:JMD_ifUUfsU,1.0:hI10vDNYz4M" />'
modulestore
()
.
update_item
(
item_location
,
data
)
modulestore
()
.
update_item
(
item_location
,
data
)
...
@@ -416,7 +422,7 @@ class TestDownloadtranscripts(Basetranscripts):
...
@@ -416,7 +422,7 @@ class TestDownloadtranscripts(Basetranscripts):
'type'
:
'videoalpha'
'type'
:
'videoalpha'
}
}
resp
=
self
.
client
.
ajax_post
(
'/xblock'
,
data
)
resp
=
self
.
client
.
ajax_post
(
'/xblock'
,
data
)
item_location
=
json
.
loads
(
resp
.
content
)
.
get
(
'id'
)
item_location
=
self
.
_get_location
(
resp
)
subs_id
=
str
(
uuid4
())
subs_id
=
str
(
uuid4
())
data
=
textwrap
.
dedent
(
"""
data
=
textwrap
.
dedent
(
"""
<videoalpha youtube="" sub="{}">
<videoalpha youtube="" sub="{}">
...
@@ -666,7 +672,7 @@ class TestChecktranscripts(Basetranscripts):
...
@@ -666,7 +672,7 @@ class TestChecktranscripts(Basetranscripts):
'type'
:
'not_video'
'type'
:
'not_video'
}
}
resp
=
self
.
client
.
ajax_post
(
'/xblock'
,
data
)
resp
=
self
.
client
.
ajax_post
(
'/xblock'
,
data
)
item_location
=
json
.
loads
(
resp
.
content
)
.
get
(
'id'
)
item_location
=
self
.
_get_location
(
resp
)
subs_id
=
str
(
uuid4
())
subs_id
=
str
(
uuid4
())
data
=
textwrap
.
dedent
(
"""
data
=
textwrap
.
dedent
(
"""
<not_video youtube="" sub="{}">
<not_video youtube="" sub="{}">
...
...
cms/djangoapps/contentstore/views/component.py
View file @
50128cfb
This diff is collapsed.
Click to expand it.
cms/djangoapps/contentstore/views/import_export.py
View file @
50128cfb
...
@@ -14,7 +14,6 @@ from django.conf import settings
...
@@ -14,7 +14,6 @@ from django.conf import settings
from
django.http
import
HttpResponse
from
django.http
import
HttpResponse
from
django.contrib.auth.decorators
import
login_required
from
django.contrib.auth.decorators
import
login_required
from
django_future.csrf
import
ensure_csrf_cookie
from
django_future.csrf
import
ensure_csrf_cookie
from
django.core.urlresolvers
import
reverse
from
django.core.servers.basehttp
import
FileWrapper
from
django.core.servers.basehttp
import
FileWrapper
from
django.core.files.temp
import
NamedTemporaryFile
from
django.core.files.temp
import
NamedTemporaryFile
from
django.core.exceptions
import
SuspiciousOperation
,
PermissionDenied
from
django.core.exceptions
import
SuspiciousOperation
,
PermissionDenied
...
@@ -140,7 +139,7 @@ def import_handler(request, tag=None, course_id=None, branch=None, version_guid=
...
@@ -140,7 +139,7 @@ def import_handler(request, tag=None, course_id=None, branch=None, version_guid=
"size"
:
size
,
"size"
:
size
,
"deleteUrl"
:
""
,
"deleteUrl"
:
""
,
"deleteType"
:
""
,
"deleteType"
:
""
,
"url"
:
location
.
url_reverse
(
'import
/'
,
'
'
),
"url"
:
location
.
url_reverse
(
'import'
),
"thumbnailUrl"
:
""
"thumbnailUrl"
:
""
}]
}]
})
})
...
@@ -252,8 +251,8 @@ def import_handler(request, tag=None, course_id=None, branch=None, version_guid=
...
@@ -252,8 +251,8 @@ def import_handler(request, tag=None, course_id=None, branch=None, version_guid=
course_module
=
modulestore
()
.
get_item
(
old_location
)
course_module
=
modulestore
()
.
get_item
(
old_location
)
return
render_to_response
(
'import.html'
,
{
return
render_to_response
(
'import.html'
,
{
'context_course'
:
course_module
,
'context_course'
:
course_module
,
'successful_import_redirect_url'
:
location
.
url_reverse
(
"course
/"
,
"
"
),
'successful_import_redirect_url'
:
location
.
url_reverse
(
"course"
),
'import_status_url'
:
location
.
url_reverse
(
"import_status
/
"
,
"fillerName"
),
'import_status_url'
:
location
.
url_reverse
(
"import_status"
,
"fillerName"
),
})
})
else
:
else
:
return
HttpResponseNotFound
()
return
HttpResponseNotFound
()
...
@@ -313,7 +312,7 @@ def export_handler(request, tag=None, course_id=None, branch=None, version_guid=
...
@@ -313,7 +312,7 @@ def export_handler(request, tag=None, course_id=None, branch=None, version_guid=
# an _accept URL parameter will be preferred over HTTP_ACCEPT in the header.
# an _accept URL parameter will be preferred over HTTP_ACCEPT in the header.
requested_format
=
request
.
REQUEST
.
get
(
'_accept'
,
request
.
META
.
get
(
'HTTP_ACCEPT'
,
'text/html'
))
requested_format
=
request
.
REQUEST
.
get
(
'_accept'
,
request
.
META
.
get
(
'HTTP_ACCEPT'
,
'text/html'
))
export_url
=
location
.
url_reverse
(
'export
/'
,
'
'
)
+
'?_accept=application/x-tgz'
export_url
=
location
.
url_reverse
(
'export'
)
+
'?_accept=application/x-tgz'
if
'application/x-tgz'
in
requested_format
:
if
'application/x-tgz'
in
requested_format
:
name
=
old_location
.
name
name
=
old_location
.
name
export_file
=
NamedTemporaryFile
(
prefix
=
name
+
'.'
,
suffix
=
".tar.gz"
)
export_file
=
NamedTemporaryFile
(
prefix
=
name
+
'.'
,
suffix
=
".tar.gz"
)
...
@@ -339,16 +338,16 @@ def export_handler(request, tag=None, course_id=None, branch=None, version_guid=
...
@@ -339,16 +338,16 @@ def export_handler(request, tag=None, course_id=None, branch=None, version_guid=
# if we have a nested exception, then we'll show the more generic error message
# if we have a nested exception, then we'll show the more generic error message
pass
pass
unit_locator
=
loc_mapper
()
.
translate_location
(
old_location
.
course_id
,
parent
.
location
,
False
,
True
)
return
render_to_response
(
'export.html'
,
{
return
render_to_response
(
'export.html'
,
{
'context_course'
:
course_module
,
'context_course'
:
course_module
,
'in_err'
:
True
,
'in_err'
:
True
,
'raw_err_msg'
:
str
(
e
),
'raw_err_msg'
:
str
(
e
),
'failed_module'
:
failed_item
,
'failed_module'
:
failed_item
,
'unit'
:
unit
,
'unit'
:
unit
,
'edit_unit_url'
:
reverse
(
'edit_unit'
,
kwargs
=
{
'edit_unit_url'
:
unit_locator
.
url_reverse
(
"unit"
)
if
parent
else
""
,
'location'
:
parent
.
location
'course_home_url'
:
location
.
url_reverse
(
"course"
),
})
if
parent
else
''
,
'course_home_url'
:
location
.
url_reverse
(
"course/"
,
""
),
'export_url'
:
export_url
'export_url'
:
export_url
})
})
...
@@ -359,7 +358,7 @@ def export_handler(request, tag=None, course_id=None, branch=None, version_guid=
...
@@ -359,7 +358,7 @@ def export_handler(request, tag=None, course_id=None, branch=None, version_guid=
'in_err'
:
True
,
'in_err'
:
True
,
'unit'
:
None
,
'unit'
:
None
,
'raw_err_msg'
:
str
(
e
),
'raw_err_msg'
:
str
(
e
),
'course_home_url'
:
location
.
url_reverse
(
"course
/"
,
"
"
),
'course_home_url'
:
location
.
url_reverse
(
"course"
),
'export_url'
:
export_url
'export_url'
:
export_url
})
})
...
...
cms/djangoapps/contentstore/views/item.py
View file @
50128cfb
...
@@ -31,7 +31,6 @@ from django.http import HttpResponseBadRequest
...
@@ -31,7 +31,6 @@ from django.http import HttpResponseBadRequest
from
xblock.fields
import
Scope
from
xblock.fields
import
Scope
from
preview
import
handler_prefix
,
get_preview_html
from
preview
import
handler_prefix
,
get_preview_html
from
mitxmako.shortcuts
import
render_to_response
,
render_to_string
from
mitxmako.shortcuts
import
render_to_response
,
render_to_string
from
django.views.decorators.csrf
import
ensure_csrf_cookie
from
models.settings.course_grading
import
CourseGradingModel
from
models.settings.course_grading
import
CourseGradingModel
__all__
=
[
'orphan_handler'
,
'xblock_handler'
]
__all__
=
[
'orphan_handler'
,
'xblock_handler'
]
...
@@ -57,7 +56,7 @@ def xblock_handler(request, tag=None, course_id=None, branch=None, version_guid=
...
@@ -57,7 +56,7 @@ def xblock_handler(request, tag=None, course_id=None, branch=None, version_guid=
all children and "all_versions" to delete from all (mongo) versions.
all children and "all_versions" to delete from all (mongo) versions.
GET
GET
json: returns representation of the xblock (locator id, data, and metadata).
json: returns representation of the xblock (locator id, data, and metadata).
if ?fields=graderType, it returns the graderType for the unit instead of the above.
if ?fields=graderType, it returns the graderType for the unit instead of the above.
html: returns HTML for rendering the xblock (which includes both the "preview" view and the "editor" view)
html: returns HTML for rendering the xblock (which includes both the "preview" view and the "editor" view)
PUT or POST
PUT or POST
json: if xblock locator is specified, update the xblock instance. The json payload can contain
json: if xblock locator is specified, update the xblock instance. The json payload can contain
...
@@ -68,6 +67,7 @@ def xblock_handler(request, tag=None, course_id=None, branch=None, version_guid=
...
@@ -68,6 +67,7 @@ def xblock_handler(request, tag=None, course_id=None, branch=None, version_guid=
to None! Absent ones will be left alone.
to None! Absent ones will be left alone.
:nullout: which metadata fields to set to None
:nullout: which metadata fields to set to None
:graderType: change how this unit is graded
:graderType: change how this unit is graded
:publish: can be one of three values, 'make_public, 'make_private', or 'create_draft'
The JSON representation on the updated xblock (minus children) is returned.
The JSON representation on the updated xblock (minus children) is returned.
if xblock locator is not specified, create a new xblock instance. The json playload can contain
if xblock locator is not specified, create a new xblock instance. The json playload can contain
...
@@ -118,13 +118,15 @@ def xblock_handler(request, tag=None, course_id=None, branch=None, version_guid=
...
@@ -118,13 +118,15 @@ def xblock_handler(request, tag=None, course_id=None, branch=None, version_guid=
return
_delete_item_at_location
(
old_location
,
delete_children
,
delete_all_versions
)
return
_delete_item_at_location
(
old_location
,
delete_children
,
delete_all_versions
)
else
:
# Since we have a course_id, we are updating an existing xblock.
else
:
# Since we have a course_id, we are updating an existing xblock.
return
_save_item
(
return
_save_item
(
request
,
locator
,
locator
,
old_location
,
old_location
,
data
=
request
.
json
.
get
(
'data'
),
data
=
request
.
json
.
get
(
'data'
),
children
=
request
.
json
.
get
(
'children'
),
children
=
request
.
json
.
get
(
'children'
),
metadata
=
request
.
json
.
get
(
'metadata'
),
metadata
=
request
.
json
.
get
(
'metadata'
),
nullout
=
request
.
json
.
get
(
'nullout'
),
nullout
=
request
.
json
.
get
(
'nullout'
),
grader_type
=
request
.
json
.
get
(
'graderType'
)
grader_type
=
request
.
json
.
get
(
'graderType'
),
publish
=
request
.
json
.
get
(
'publish'
),
)
)
elif
request
.
method
in
(
'PUT'
,
'POST'
):
elif
request
.
method
in
(
'PUT'
,
'POST'
):
return
_create_item
(
request
)
return
_create_item
(
request
)
...
@@ -135,11 +137,10 @@ def xblock_handler(request, tag=None, course_id=None, branch=None, version_guid=
...
@@ -135,11 +137,10 @@ def xblock_handler(request, tag=None, course_id=None, branch=None, version_guid=
)
)
def
_save_item
(
usage_loc
,
item_location
,
data
=
None
,
children
=
None
,
metadata
=
None
,
nullout
=
None
,
def
_save_item
(
request
,
usage_loc
,
item_location
,
data
=
None
,
children
=
None
,
metadata
=
None
,
nullout
=
None
,
grader_type
=
None
grader_type
=
None
,
publish
=
None
):
):
"""
"""
Saves xblock w/ its fields. Has special processing for grader_type and nullout and Nones in metadata.
Saves xblock w/ its fields. Has special processing for grader_type
, publish,
and nullout and Nones in metadata.
nullout means to truly set the field to None whereas nones in metadata mean to unset them (so they revert
nullout means to truly set the field to None whereas nones in metadata mean to unset them (so they revert
to default).
to default).
...
@@ -161,6 +162,14 @@ def _save_item(usage_loc, item_location, data=None, children=None, metadata=None
...
@@ -161,6 +162,14 @@ def _save_item(usage_loc, item_location, data=None, children=None, metadata=None
log
.
error
(
"Can't find item by location."
)
log
.
error
(
"Can't find item by location."
)
return
JsonResponse
({
"error"
:
"Can't find item by location: "
+
str
(
item_location
)},
404
)
return
JsonResponse
({
"error"
:
"Can't find item by location: "
+
str
(
item_location
)},
404
)
if
publish
:
if
publish
==
'make_private'
:
_xmodule_recurse
(
existing_item
,
lambda
i
:
modulestore
()
.
unpublish
(
i
.
location
))
elif
publish
==
'create_draft'
:
# This clones the existing item location to a draft location (the draft is
# implicit, because modulestore is a Draft modulestore)
modulestore
()
.
convert_to_draft
(
item_location
)
if
data
:
if
data
:
store
.
update_item
(
item_location
,
data
)
store
.
update_item
(
item_location
,
data
)
else
:
else
:
...
@@ -213,9 +222,18 @@ def _save_item(usage_loc, item_location, data=None, children=None, metadata=None
...
@@ -213,9 +222,18 @@ def _save_item(usage_loc, item_location, data=None, children=None, metadata=None
'data'
:
data
,
'data'
:
data
,
'metadata'
:
own_metadata
(
existing_item
)
'metadata'
:
own_metadata
(
existing_item
)
}
}
if
grader_type
is
not
None
:
if
grader_type
is
not
None
:
result
.
update
(
CourseGradingModel
.
update_section_grader_type
(
existing_item
,
grader_type
))
result
.
update
(
CourseGradingModel
.
update_section_grader_type
(
existing_item
,
grader_type
))
# Make public after updating the xblock, in case the caller asked
# for both an update and a publish.
if
publish
and
publish
==
'make_public'
:
_xmodule_recurse
(
existing_item
,
lambda
i
:
modulestore
()
.
publish
(
i
.
location
,
request
.
user
.
id
)
)
# Note that children aren't being returned until we have a use case.
# Note that children aren't being returned until we have a use case.
return
JsonResponse
(
result
)
return
JsonResponse
(
result
)
...
@@ -234,10 +252,7 @@ def _create_item(request):
...
@@ -234,10 +252,7 @@ def _create_item(request):
raise
PermissionDenied
()
raise
PermissionDenied
()
parent
=
get_modulestore
(
category
)
.
get_item
(
parent_location
)
parent
=
get_modulestore
(
category
)
.
get_item
(
parent_location
)
# Necessary to set revision=None or else metadata inheritance does not work
dest_location
=
parent_location
.
replace
(
category
=
category
,
name
=
uuid4
()
.
hex
)
# (the ID with @draft will be used as the key in the inherited metadata map,
# and that is not expected by the code that later references it).
dest_location
=
parent_location
.
replace
(
category
=
category
,
name
=
uuid4
()
.
hex
,
revision
=
None
)
# get the metadata, display_name, and definition from the request
# get the metadata, display_name, and definition from the request
metadata
=
{}
metadata
=
{}
...
@@ -266,7 +281,7 @@ def _create_item(request):
...
@@ -266,7 +281,7 @@ def _create_item(request):
course_location
=
loc_mapper
()
.
translate_locator_to_location
(
parent_locator
,
get_course
=
True
)
course_location
=
loc_mapper
()
.
translate_locator_to_location
(
parent_locator
,
get_course
=
True
)
locator
=
loc_mapper
()
.
translate_location
(
course_location
.
course_id
,
dest_location
,
False
,
True
)
locator
=
loc_mapper
()
.
translate_location
(
course_location
.
course_id
,
dest_location
,
False
,
True
)
return
JsonResponse
({
'id'
:
dest_location
.
url
(),
"locator"
:
unicode
(
locator
)})
return
JsonResponse
({
"locator"
:
unicode
(
locator
)})
def
_delete_item_at_location
(
item_location
,
delete_children
=
False
,
delete_all_versions
=
False
):
def
_delete_item_at_location
(
item_location
,
delete_children
=
False
,
delete_all_versions
=
False
):
...
...
cms/static/coffee/src/views/unit.coffee
View file @
50128cfb
...
@@ -166,7 +166,7 @@ define ["jquery", "jquery.ui", "gettext", "backbone",
...
@@ -166,7 +166,7 @@ define ["jquery", "jquery.ui", "gettext", "backbone",
@
wait
(
true
)
@
wait
(
true
)
$
.
ajax
({
$
.
ajax
({
type
:
'DELETE'
,
type
:
'DELETE'
,
url
:
@
model
.
url
Root
+
"/"
+
@
$el
.
data
(
'locator'
)
+
"?"
+
$
.
param
({
recurse
:
true
})
url
:
@
model
.
url
(
)
+
"?"
+
$
.
param
({
recurse
:
true
})
}).
success
(
=>
}).
success
(
=>
analytics
.
track
"Deleted Draft"
,
analytics
.
track
"Deleted Draft"
,
...
@@ -179,8 +179,8 @@ define ["jquery", "jquery.ui", "gettext", "backbone",
...
@@ -179,8 +179,8 @@ define ["jquery", "jquery.ui", "gettext", "backbone",
createDraft
:
(
event
)
->
createDraft
:
(
event
)
->
@
wait
(
true
)
@
wait
(
true
)
$
.
postJSON
(
'/create_draft'
,
{
$
.
postJSON
(
@
model
.
url
()
,
{
id
:
@
$el
.
data
(
'id'
)
publish
:
'create_draft'
},
=>
},
=>
analytics
.
track
"Created Draft"
,
analytics
.
track
"Created Draft"
,
course
:
course_location_analytics
course
:
course_location_analytics
...
@@ -193,8 +193,8 @@ define ["jquery", "jquery.ui", "gettext", "backbone",
...
@@ -193,8 +193,8 @@ define ["jquery", "jquery.ui", "gettext", "backbone",
@
wait
(
true
)
@
wait
(
true
)
@
saveDraft
()
@
saveDraft
()
$
.
postJSON
(
'/publish_draft'
,
{
$
.
postJSON
(
@
model
.
url
()
,
{
id
:
@
$el
.
data
(
'id'
)
publish
:
'make_public'
},
=>
},
=>
analytics
.
track
"Published Draft"
,
analytics
.
track
"Published Draft"
,
course
:
course_location_analytics
course
:
course_location_analytics
...
@@ -205,16 +205,16 @@ define ["jquery", "jquery.ui", "gettext", "backbone",
...
@@ -205,16 +205,16 @@ define ["jquery", "jquery.ui", "gettext", "backbone",
setVisibility
:
(
event
)
->
setVisibility
:
(
event
)
->
if
@
$
(
'.visibility-select'
).
val
()
==
'private'
if
@
$
(
'.visibility-select'
).
val
()
==
'private'
target_url
=
'/unpublish_unit
'
action
=
'make_private
'
visibility
=
"private"
visibility
=
"private"
else
else
target_url
=
'/publish_draft
'
action
=
'make_public
'
visibility
=
"public"
visibility
=
"public"
@
wait
(
true
)
@
wait
(
true
)
$
.
postJSON
(
target_url
,
{
$
.
postJSON
(
@
model
.
url
()
,
{
id
:
@
$el
.
data
(
'id'
)
publish
:
action
},
=>
},
=>
analytics
.
track
"Set Unit Visibility"
,
analytics
.
track
"Set Unit Visibility"
,
course
:
course_location_analytics
course
:
course_location_analytics
...
...
cms/static/js/base.js
View file @
50128cfb
...
@@ -237,7 +237,7 @@ function createNewUnit(e) {
...
@@ -237,7 +237,7 @@ function createNewUnit(e) {
function
(
data
)
{
function
(
data
)
{
// redirect to the edit page
// redirect to the edit page
window
.
location
=
"/
edit/"
+
data
[
'id
'
];
window
.
location
=
"/
unit/"
+
data
[
'locator
'
];
});
});
}
}
...
...
cms/templates/overview.html
View file @
50128cfb
...
@@ -207,7 +207,7 @@ require(["domReady!", "jquery", "js/models/location", "js/models/section", "js/v
...
@@ -207,7 +207,7 @@ require(["domReady!", "jquery", "js/models/location", "js/models/section", "js/v
<div
class=
"section-item"
>
<div
class=
"section-item"
>
<div
class=
"details"
>
<div
class=
"details"
>
<a
href=
"#"
data-tooltip=
"${_('Expand/collapse this subsection')}"
class=
"expand-collapse-icon expand"
></a>
<a
href=
"#"
data-tooltip=
"${_('Expand/collapse this subsection')}"
class=
"expand-collapse-icon expand"
></a>
<a
href=
"${
reverse('edit_subsection', args=[subsection.location]
)}"
>
<a
href=
"${
subsection_locator.url_reverse('subsection'
)}"
>
<span
class=
"folder-icon"
></span>
<span
class=
"folder-icon"
></span>
<span
class=
"subsection-name"
><span
class=
"subsection-name-value"
>
${subsection.display_name_with_default}
</span></span>
<span
class=
"subsection-name"
><span
class=
"subsection-name-value"
>
${subsection.display_name_with_default}
</span></span>
</a>
</a>
...
...
cms/templates/unit.html
View file @
50128cfb
...
@@ -34,7 +34,7 @@ require(["domReady!", "jquery", "js/models/module_info", "coffee/src/views/unit"
...
@@ -34,7 +34,7 @@ require(["domReady!", "jquery", "js/models/module_info", "coffee/src/views/unit"
</
%
block>
</
%
block>
<
%
block
name=
"content"
>
<
%
block
name=
"content"
>
<div
class=
"main-wrapper edit-state-${unit_state}"
data-
id=
"${unit_location}"
data-
locator=
"${unit_locator}"
>
<div
class=
"main-wrapper edit-state-${unit_state}"
data-locator=
"${unit_locator}"
>
<div
class=
"inner-wrapper"
>
<div
class=
"inner-wrapper"
>
<div
class=
"alert editing-draft-alert"
>
<div
class=
"alert editing-draft-alert"
>
<p
class=
"alert-message"
><strong>
${_("You are editing a draft.")}
</strong>
<p
class=
"alert-message"
><strong>
${_("You are editing a draft.")}
</strong>
...
@@ -135,6 +135,13 @@ require(["domReady!", "jquery", "js/models/module_info", "coffee/src/views/unit"
...
@@ -135,6 +135,13 @@ require(["domReady!", "jquery", "js/models/module_info", "coffee/src/views/unit"
</article>
</article>
</div>
</div>
<
%
ctx_loc =
context_course.location
index_url =
loc_mapper().translate_location(ctx_loc.course_id,
ctx_loc
,
False
,
True
).
url_reverse
('
course
')
subsection_url =
loc_mapper().translate_location(
ctx_loc
.
course_id
,
subsection
.
location
,
False
,
True
).
url_reverse
('
subsection
')
%
>
<div
class=
"sidebar"
>
<div
class=
"sidebar"
>
<div
class=
"unit-settings window"
>
<div
class=
"unit-settings window"
>
<h4
class=
"header"
>
${_("Unit Settings")}
</h4>
<h4
class=
"header"
>
${_("Unit Settings")}
</h4>
...
@@ -157,7 +164,7 @@ require(["domReady!", "jquery", "js/models/module_info", "coffee/src/views/unit"
...
@@ -157,7 +164,7 @@ require(["domReady!", "jquery", "js/models/module_info", "coffee/src/views/unit"
% endif
% endif
${_("with the subsection {link_start}{name}{link_end}").format(
${_("with the subsection {link_start}{name}{link_end}").format(
name=subsection.display_name_with_default,
name=subsection.display_name_with_default,
link_start='
<a
href=
"{url}"
>
'.format(url=
reverse('edit_subsection', kwargs={'location': subsection.location})
),
link_start='
<a
href=
"{url}"
>
'.format(url=
subsection_url
),
link_end='
</a>
',
link_end='
</a>
',
)}
)}
</p>
</p>
...
@@ -180,14 +187,10 @@ require(["domReady!", "jquery", "js/models/module_info", "coffee/src/views/unit"
...
@@ -180,14 +187,10 @@ require(["domReady!", "jquery", "js/models/module_info", "coffee/src/views/unit"
</div>
</div>
<ol>
<ol>
<li>
<li>
<
%
ctx_loc =
context_course.location
index_url =
loc_mapper().translate_location(ctx_loc.course_id,
ctx_loc
,
False
,
True
).
url_reverse
('
course
/',
'')
%
>
<a
href=
"${index_url}"
class=
"section-item"
>
${section.display_name_with_default}
</a>
<a
href=
"${index_url}"
class=
"section-item"
>
${section.display_name_with_default}
</a>
<ol>
<ol>
<li>
<li>
<a
href=
"${
reverse('edit_subsection', args=[subsection.location])
}"
class=
"section-item"
>
<a
href=
"${
subsection_url
}"
class=
"section-item"
>
<span
class=
"folder-icon"
></span>
<span
class=
"folder-icon"
></span>
<span
class=
"subsection-name"
><span
class=
"subsection-name-value"
>
${subsection.display_name_with_default}
</span></span>
<span
class=
"subsection-name"
><span
class=
"subsection-name-value"
>
${subsection.display_name_with_default}
</span></span>
</a>
</a>
...
...
cms/templates/widgets/units.html
View file @
50128cfb
...
@@ -31,7 +31,7 @@ This def will enumerate through a passed in subsection and list all of the units
...
@@ -31,7 +31,7 @@ This def will enumerate through a passed in subsection and list all of the units
selected_class =
''
selected_class =
''
%
>
%
>
<div
class=
"section-item ${selected_class}"
>
<div
class=
"section-item ${selected_class}"
>
<a
href=
"${
reverse('edit_unit', args=[unit.location]
)}"
class=
"${unit_state}-item"
>
<a
href=
"${
unit_locator.url_reverse('unit'
)}"
class=
"${unit_state}-item"
>
<span
class=
"${unit.scope_ids.block_type}-icon"
></span>
<span
class=
"${unit.scope_ids.block_type}-icon"
></span>
<span
class=
"unit-name"
>
${unit.display_name_with_default}
</span>
<span
class=
"unit-name"
>
${unit.display_name_with_default}
</span>
</a>
</a>
...
...
cms/urls.py
View file @
50128cfb
...
@@ -11,8 +11,6 @@ from ratelimitbackend import admin
...
@@ -11,8 +11,6 @@ from ratelimitbackend import admin
admin
.
autodiscover
()
admin
.
autodiscover
()
urlpatterns
=
patterns
(
''
,
# nopep8
urlpatterns
=
patterns
(
''
,
# nopep8
url
(
r'^edit/(?P<location>.*?)$'
,
'contentstore.views.edit_unit'
,
name
=
'edit_unit'
),
url
(
r'^subsection/(?P<location>.*?)$'
,
'contentstore.views.edit_subsection'
,
name
=
'edit_subsection'
),
url
(
r'^transcripts/upload$'
,
'contentstore.views.upload_transcripts'
,
name
=
'upload_transcripts'
),
url
(
r'^transcripts/upload$'
,
'contentstore.views.upload_transcripts'
,
name
=
'upload_transcripts'
),
url
(
r'^transcripts/download$'
,
'contentstore.views.download_transcripts'
,
name
=
'download_transcripts'
),
url
(
r'^transcripts/download$'
,
'contentstore.views.download_transcripts'
,
name
=
'download_transcripts'
),
...
@@ -22,10 +20,6 @@ urlpatterns = patterns('', # nopep8
...
@@ -22,10 +20,6 @@ urlpatterns = patterns('', # nopep8
url
(
r'^transcripts/rename$'
,
'contentstore.views.rename_transcripts'
,
name
=
'rename_transcripts'
),
url
(
r'^transcripts/rename$'
,
'contentstore.views.rename_transcripts'
,
name
=
'rename_transcripts'
),
url
(
r'^transcripts/save$'
,
'contentstore.views.save_transcripts'
,
name
=
'save_transcripts'
),
url
(
r'^transcripts/save$'
,
'contentstore.views.save_transcripts'
,
name
=
'save_transcripts'
),
url
(
r'^create_draft$'
,
'contentstore.views.create_draft'
,
name
=
'create_draft'
),
url
(
r'^publish_draft$'
,
'contentstore.views.publish_draft'
,
name
=
'publish_draft'
),
url
(
r'^unpublish_unit$'
,
'contentstore.views.unpublish_unit'
,
name
=
'unpublish_unit'
),
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'
),
...
@@ -89,6 +83,8 @@ urlpatterns += patterns(
...
@@ -89,6 +83,8 @@ urlpatterns += patterns(
'course_info_update_handler'
'course_info_update_handler'
),
),
url
(
r'(?ix)^course($|/){}$'
.
format
(
parsers
.
URL_RE_SOURCE
),
'course_handler'
),
url
(
r'(?ix)^course($|/){}$'
.
format
(
parsers
.
URL_RE_SOURCE
),
'course_handler'
),
url
(
r'(?ix)^subsection($|/){}$'
.
format
(
parsers
.
URL_RE_SOURCE
),
'subsection_handler'
),
url
(
r'(?ix)^unit($|/){}$'
.
format
(
parsers
.
URL_RE_SOURCE
),
'unit_handler'
),
url
(
r'(?ix)^checklists/{}(/)?(?P<checklist_index>\d+)?$'
.
format
(
parsers
.
URL_RE_SOURCE
),
'checklists_handler'
),
url
(
r'(?ix)^checklists/{}(/)?(?P<checklist_index>\d+)?$'
.
format
(
parsers
.
URL_RE_SOURCE
),
'checklists_handler'
),
url
(
r'(?ix)^orphan/{}$'
.
format
(
parsers
.
URL_RE_SOURCE
),
'orphan_handler'
),
url
(
r'(?ix)^orphan/{}$'
.
format
(
parsers
.
URL_RE_SOURCE
),
'orphan_handler'
),
url
(
r'(?ix)^assets/{}(/)?(?P<asset_id>.+)?$'
.
format
(
parsers
.
URL_RE_SOURCE
),
'assets_handler'
),
url
(
r'(?ix)^assets/{}(/)?(?P<asset_id>.+)?$'
.
format
(
parsers
.
URL_RE_SOURCE
),
'assets_handler'
),
...
...
common/lib/xmodule/xmodule/modulestore/loc_mapper_store.py
View file @
50128cfb
...
@@ -204,21 +204,16 @@ class LocMapperStore(object):
...
@@ -204,21 +204,16 @@ class LocMapperStore(object):
self
.
_decode_from_mongo
(
old_name
),
self
.
_decode_from_mongo
(
old_name
),
None
)
None
)
elif
usage_id
==
locator
.
usage_id
:
elif
usage_id
==
locator
.
usage_id
:
# figure out revision
# Always return revision=None because the
# enforce the draft only if category in [..] logic
# old draft module store wraps locations as draft before
if
category
in
draft
.
DIRECT_ONLY_CATEGORIES
:
# trying to access things.
revision
=
None
elif
locator
.
branch
==
candidate
[
'draft_branch'
]:
revision
=
draft
.
DRAFT
else
:
revision
=
None
return
Location
(
return
Location
(
'i4x'
,
'i4x'
,
candidate
[
'_id'
][
'org'
],
candidate
[
'_id'
][
'org'
],
candidate
[
'_id'
][
'course'
],
candidate
[
'_id'
][
'course'
],
category
,
category
,
self
.
_decode_from_mongo
(
old_name
),
self
.
_decode_from_mongo
(
old_name
),
revision
)
None
)
return
None
return
None
def
add_block_location_translator
(
self
,
location
,
old_course_id
=
None
,
usage_id
=
None
):
def
add_block_location_translator
(
self
,
location
,
old_course_id
=
None
,
usage_id
=
None
):
...
...
common/lib/xmodule/xmodule/modulestore/mongo/base.py
View file @
50128cfb
...
@@ -778,11 +778,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -778,11 +778,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
children: A list of child item identifiers
children: A list of child item identifiers
"""
"""
# We expect the children IDs to always be the non-draft version. With view refactoring
self
.
_update_single_item
(
location
,
{
'definition.children'
:
children
})
# for split, we are now passing the draft version in some cases.
children_ids
=
[
Location
(
child
)
.
replace
(
revision
=
None
)
.
url
()
for
child
in
children
]
self
.
_update_single_item
(
location
,
{
'definition.children'
:
children_ids
})
# recompute (and update) the metadata inheritance tree which is cached
# recompute (and update) the metadata inheritance tree which is cached
self
.
refresh_cached_metadata_inheritance_tree
(
Location
(
location
))
self
.
refresh_cached_metadata_inheritance_tree
(
Location
(
location
))
# fire signal that we've written to DB
# fire signal that we've written to DB
...
...
common/lib/xmodule/xmodule/modulestore/tests/test_location_mapper.py
View file @
50128cfb
...
@@ -274,7 +274,9 @@ class TestLocationMapper(unittest.TestCase):
...
@@ -274,7 +274,9 @@ class TestLocationMapper(unittest.TestCase):
course_id
=
prob_locator
.
course_id
,
branch
=
'draft'
,
usage_id
=
prob_locator
.
usage_id
course_id
=
prob_locator
.
course_id
,
branch
=
'draft'
,
usage_id
=
prob_locator
.
usage_id
)
)
prob_location
=
loc_mapper
()
.
translate_locator_to_location
(
prob_locator
)
prob_location
=
loc_mapper
()
.
translate_locator_to_location
(
prob_locator
)
self
.
assertEqual
(
prob_location
,
Location
(
'i4x'
,
org
,
course
,
'problem'
,
'abc123'
,
'draft'
))
# Even though the problem was set as draft, we always return revision=None to work
# with old mongo/draft modulestores.
self
.
assertEqual
(
prob_location
,
Location
(
'i4x'
,
org
,
course
,
'problem'
,
'abc123'
,
None
))
prob_locator
=
BlockUsageLocator
(
prob_locator
=
BlockUsageLocator
(
course_id
=
new_style_course_id
,
usage_id
=
'problem2'
,
branch
=
'production'
course_id
=
new_style_course_id
,
usage_id
=
'problem2'
,
branch
=
'production'
)
)
...
...
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