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
52f8c976
Commit
52f8c976
authored
Dec 28, 2016
by
Mushtaq Ali
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Move an item - TNL-6064
parent
3f355241
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
122 additions
and
6 deletions
+122
-6
cms/djangoapps/contentstore/views/item.py
+122
-6
cms/djangoapps/contentstore/views/tests/test_item.py
+0
-0
No files found.
cms/djangoapps/contentstore/views/item.py
View file @
52f8c976
...
...
@@ -19,6 +19,8 @@ from django.views.decorators.http import require_http_methods
from
opaque_keys.edx.keys
import
CourseKey
from
opaque_keys.edx.locator
import
LibraryUsageLocator
from
pytz
import
UTC
from
xblock.core
import
XBlock
from
xblock.fields
import
Scope
from
xblock.fragment
import
Fragment
from
xblock_django.user_service
import
DjangoXBlockUserService
...
...
@@ -127,8 +129,11 @@ def xblock_handler(request, usage_key_string):
if usage_key_string is not specified, create a new xblock instance, either by duplicating
an existing xblock, or creating an entirely new one. The json playload can contain
these fields:
:parent_locator: parent for new xblock, required for
both duplicat
e and create new instance
:parent_locator: parent for new xblock, required for
duplicate, mov
e and create new instance
:duplicate_source_locator: if present, use this as the source for creating a duplicate copy
:move_source_locator: if present, use this as the source item for moving
:target_index: if present, use this as the target index for moving an item to a particular index
otherwise target_index is calculated. It is sent back in the response.
:category: type of xblock, required if duplicate_source_locator is not present.
:display_name: name for new xblock, optional
:boilerplate: template name for populating fields, optional and only used
...
...
@@ -198,14 +203,26 @@ def xblock_handler(request, usage_key_string):
request
.
user
,
request
.
json
.
get
(
'display_name'
),
)
return
JsonResponse
({
"locator"
:
unicode
(
dest_usage_key
),
"courseKey"
:
unicode
(
dest_usage_key
.
course_key
)})
return
JsonResponse
({
'locator'
:
unicode
(
dest_usage_key
),
'courseKey'
:
unicode
(
dest_usage_key
.
course_key
)})
else
:
return
_create_item
(
request
)
elif
request
.
method
==
'PATCH'
:
if
'move_source_locator'
in
request
.
json
:
move_source_usage_key
=
usage_key_with_run
(
request
.
json
.
get
(
'move_source_locator'
))
target_parent_usage_key
=
usage_key_with_run
(
request
.
json
.
get
(
'parent_locator'
))
target_index
=
request
.
json
.
get
(
'target_index'
)
if
(
not
has_studio_write_access
(
request
.
user
,
target_parent_usage_key
.
course_key
)
or
not
has_studio_read_access
(
request
.
user
,
target_parent_usage_key
.
course_key
)
):
raise
PermissionDenied
()
return
_move_item
(
move_source_usage_key
,
target_parent_usage_key
,
request
.
user
,
target_index
)
return
JsonResponse
({
'error'
:
'Patch request did not recognise any parameters to handle.'
},
status
=
400
)
else
:
return
HttpResponseBadRequest
(
"Only instance creation is supported without a usage key."
,
content_type
=
"text/plain"
'Only instance creation is supported without a usage key.'
,
content_type
=
'text/plain'
)
...
...
@@ -636,10 +653,109 @@ def _create_item(request):
)
return
JsonResponse
(
{
"locator"
:
unicode
(
created_block
.
location
),
"courseKey"
:
unicode
(
created_block
.
location
.
course_key
)}
{
'locator'
:
unicode
(
created_block
.
location
),
'courseKey'
:
unicode
(
created_block
.
location
.
course_key
)}
)
def
_get_source_index
(
source_usage_key
,
source_parent
):
"""
Get source index position of the XBlock.
Arguments:
source_usage_key (BlockUsageLocator): Locator of source item.
source_parent (XBlock): A parent of the source XBlock.
Returns:
source_index (int): Index position of the xblock in a parent.
"""
try
:
source_index
=
source_parent
.
children
.
index
(
source_usage_key
)
return
source_index
except
ValueError
:
return
None
def
_move_item
(
source_usage_key
,
target_parent_usage_key
,
user
,
target_index
=
None
):
"""
Move an existing xblock as a child of the supplied target_parent_usage_key.
Arguments:
source_usage_key (BlockUsageLocator): Locator of source item.
target_parent_usage_key (BlockUsageLocator): Locator of target parent.
target_index (int): If provided, insert source item at provided index location in target_parent_usage_key item.
Returns:
JsonResponse: Information regarding move operation. It may contains error info if an invalid move operation
is performed.
"""
# Get the list of all component type XBlocks
component_types
=
sorted
(
set
(
name
for
name
,
class_
in
XBlock
.
load_classes
())
-
set
(
DIRECT_ONLY_CATEGORIES
))
store
=
modulestore
()
with
store
.
bulk_operations
(
source_usage_key
.
course_key
):
source_item
=
store
.
get_item
(
source_usage_key
)
source_parent
=
source_item
.
get_parent
()
target_parent
=
store
.
get_item
(
target_parent_usage_key
)
source_type
=
source_item
.
category
target_parent_type
=
target_parent
.
category
error
=
None
# Store actual/initial index of the source item. This would be sent back with response,
# so that with Undo operation, it would easier to move back item to it's original/old index.
source_index
=
_get_source_index
(
source_usage_key
,
source_parent
)
valid_move_type
=
{
'vertical'
:
source_type
if
source_type
in
component_types
else
'component'
,
'sequential'
:
'vertical'
,
'chapter'
:
'sequential'
,
}
if
valid_move_type
.
get
(
target_parent_type
,
''
)
!=
source_type
:
error
=
'You can not move {source_type} into {target_parent_type}.'
.
format
(
source_type
=
source_type
,
target_parent_type
=
target_parent_type
,
)
elif
source_parent
.
location
==
target_parent
.
location
:
error
=
'You can not move an item into the same parent.'
elif
source_index
is
None
:
error
=
'{source_usage_key} not found in {parent_usage_key}.'
.
format
(
source_usage_key
=
unicode
(
source_usage_key
),
parent_usage_key
=
unicode
(
source_parent
.
location
)
)
else
:
try
:
target_index
=
int
(
target_index
)
if
target_index
is
not
None
else
None
if
len
(
target_parent
.
children
)
<
target_index
:
error
=
'You can not move {source_usage_key} at an invalid index ({target_index}).'
.
format
(
source_usage_key
=
unicode
(
source_usage_key
),
target_index
=
target_index
)
except
ValueError
:
error
=
'You must provide target_index ({target_index}) as an integer.'
.
format
(
target_index
=
target_index
)
if
error
:
return
JsonResponse
({
'error'
:
error
},
status
=
400
)
# Remove reference from old parent.
source_parent
.
children
.
remove
(
source_item
.
location
)
store
.
update_item
(
source_parent
,
user
.
id
)
# When target_index is provided, insert xblock at target_index position, otherwise insert at the end.
insert_at
=
target_index
if
target_index
is
not
None
else
len
(
target_parent
.
children
)
# Add to new parent at particular location.
target_parent
.
children
.
insert
(
insert_at
,
source_item
.
location
)
store
.
update_item
(
target_parent
,
user
.
id
)
context
=
{
'move_source_locator'
:
unicode
(
source_usage_key
),
'parent_locator'
:
unicode
(
target_parent_usage_key
),
'source_index'
:
target_index
if
target_index
is
not
None
else
source_index
}
return
JsonResponse
(
context
)
def
_duplicate_item
(
parent_usage_key
,
duplicate_source_usage_key
,
user
,
display_name
=
None
,
is_child
=
False
):
"""
Duplicate an existing xblock as a child of the supplied parent_usage_key.
...
...
cms/djangoapps/contentstore/views/tests/test_item.py
View file @
52f8c976
This diff is collapsed.
Click to expand it.
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