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
485ffb1b
Commit
485ffb1b
authored
Feb 02, 2017
by
Mushtaq Ali
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
restrict move action
parent
e9b8e17f
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
312 additions
and
80 deletions
+312
-80
cms/djangoapps/contentstore/utils.py
+17
-0
cms/djangoapps/contentstore/views/item.py
+33
-6
cms/djangoapps/contentstore/views/preview.py
+1
-0
cms/djangoapps/contentstore/views/tests/test_container_page.py
+42
-2
cms/djangoapps/contentstore/views/tests/test_item.py
+0
-0
cms/djangoapps/contentstore/views/tests/utils.py
+35
-21
cms/static/js/spec/views/move_xblock_spec.js
+114
-12
cms/static/js/views/modals/move_xblock_modal.js
+21
-2
cms/static/js/views/move_xblock_list.js
+25
-24
cms/static/sass/views/_container.scss
+7
-6
cms/templates/js/move-xblock-list.underscore
+13
-6
cms/templates/studio_xblock_wrapper.html
+2
-1
common/lib/xmodule/xmodule/library_content_module.py
+1
-0
common/lib/xmodule/xmodule/library_root_xblock.py
+1
-0
No files found.
cms/djangoapps/contentstore/utils.py
View file @
485ffb1b
...
...
@@ -283,6 +283,23 @@ def reverse_usage_url(handler_name, usage_key, kwargs=None):
return
reverse_url
(
handler_name
,
'usage_key_string'
,
usage_key
,
kwargs
)
def
get_group_display_name
(
user_partitions
,
xblock_display_name
):
"""
Get the group name if matching group xblock is found.
Arguments:
user_partitions (Dict): Locator of source item.
xblock_display_name (String): Display name of group xblock.
Returns:
group name (String): Group name of the matching group.
"""
for
user_partition
in
user_partitions
:
for
group
in
user_partition
[
'groups'
]:
if
str
(
group
[
'id'
])
in
xblock_display_name
:
return
group
[
'name'
]
def
get_user_partition_info
(
xblock
,
schemes
=
None
,
course
=
None
):
"""
Retrieve user partition information for an XBlock for display in editors.
...
...
cms/djangoapps/contentstore/views/item.py
View file @
485ffb1b
...
...
@@ -29,7 +29,7 @@ from cms.lib.xblock.authoring_mixin import VISIBILITY_VIEW
from
contentstore.utils
import
(
find_release_date_source
,
find_staff_lock_source
,
is_currently_visible_to_students
,
ancestor_has_staff_lock
,
has_children_visible_to_specific_content_groups
,
get_user_partition_info
,
get_user_partition_info
,
get_group_display_name
,
)
from
contentstore.views.helpers
import
is_unit
,
xblock_studio_url
,
xblock_primary_child_category
,
\
xblock_type_display_name
,
get_parent_xblock
,
create_xblock
,
usage_key_with_run
...
...
@@ -675,6 +675,21 @@ def _get_source_index(source_usage_key, source_parent):
return
None
def
is_source_item_in_target_parents
(
source_item
,
target_parent
):
"""
Returns True if source item is found in target parents otherwise False.
Arguments:
source_item (XBlock): Source Xblock.
target_parent (XBlock): Target XBlock.
"""
target_ancestors
=
_create_xblock_ancestor_info
(
target_parent
,
is_concise
=
True
)[
'ancestors'
]
for
target_ancestor
in
target_ancestors
:
if
unicode
(
source_item
.
location
)
==
target_ancestor
[
'id'
]:
return
True
return
False
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.
...
...
@@ -688,8 +703,11 @@ def _move_item(source_usage_key, target_parent_usage_key, user, target_index=Non
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
))
# Get the list of all parentable component type XBlocks.
parent_component_types
=
list
(
set
(
name
for
name
,
class_
in
XBlock
.
load_classes
()
if
getattr
(
class_
,
'has_children'
,
False
))
-
set
(
DIRECT_ONLY_CATEGORIES
)
)
store
=
modulestore
()
with
store
.
bulk_operations
(
source_usage_key
.
course_key
):
...
...
@@ -705,18 +723,22 @@ def _move_item(source_usage_key, target_parent_usage_key, user, target_index=Non
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
:
if
(
valid_move_type
.
get
(
target_parent_type
,
''
)
!=
source_type
and
target_parent_type
not
in
parent_component_types
):
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_item
.
location
==
target_parent
.
location
:
error
=
'You can not move an item into itself.'
elif
is_source_item_in_target_parents
(
source_item
,
target_parent
):
error
=
'You can not move an item into it
\'
s child.'
elif
source_index
is
None
:
error
=
'{source_usage_key} not found in {parent_usage_key}.'
.
format
(
source_usage_key
=
unicode
(
source_usage_key
),
...
...
@@ -1093,6 +1115,7 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
# a percent value out of 100, e.g. "58%" means "58/100".
pct_sign
=
_
(
'
%
'
))
user_partitions
=
get_user_partition_info
(
xblock
,
course
=
course
)
xblock_info
=
{
'id'
:
unicode
(
xblock
.
location
),
'display_name'
:
xblock
.
display_name_with_default
,
...
...
@@ -1101,6 +1124,10 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
if
is_concise
:
if
child_info
and
len
(
child_info
.
get
(
'children'
,
[]))
>
0
:
xblock_info
[
'child_info'
]
=
child_info
# Groups are labelled with their internal ids, rather than with the group name. Replace id with display name.
group_display_name
=
get_group_display_name
(
user_partitions
,
xblock_info
[
'display_name'
])
xblock_info
[
'display_name'
]
=
group_display_name
if
group_display_name
else
xblock_info
[
'display_name'
]
xblock_info
[
'has_children'
]
=
xblock
.
has_children
else
:
xblock_info
.
update
({
'edited_on'
:
get_default_time_display
(
xblock
.
subtree_edited_on
)
if
xblock
.
subtree_edited_on
else
None
,
...
...
@@ -1121,7 +1148,7 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
'actions'
:
xblock_actions
,
'explanatory_message'
:
explanatory_message
,
'group_access'
:
xblock
.
group_access
,
'user_partitions'
:
get_user_partition_info
(
xblock
,
course
=
course
)
,
'user_partitions'
:
user_partitions
,
})
if
xblock
.
category
==
'sequential'
:
...
...
cms/djangoapps/contentstore/views/preview.py
View file @
485ffb1b
...
...
@@ -274,6 +274,7 @@ def _studio_wrap_xblock(xblock, view, frag, context, display_name_only=False):
'can_edit'
:
context
.
get
(
'can_edit'
,
True
),
'can_edit_visibility'
:
context
.
get
(
'can_edit_visibility'
,
True
),
'can_add'
:
context
.
get
(
'can_add'
,
True
),
'can_move'
:
context
.
get
(
'can_move'
,
True
)
}
html
=
render_to_string
(
'studio_xblock_wrapper.html'
,
template_context
)
frag
=
wrap_fragment
(
frag
,
html
)
...
...
cms/djangoapps/contentstore/views/tests/test_container_page.py
View file @
485ffb1b
...
...
@@ -12,11 +12,13 @@ from django.utils import http
import
contentstore.views.component
as
views
from
contentstore.views.tests.utils
import
StudioPageTestCase
from
contentstore.tests.test_libraries
import
LibraryTestCase
from
xmodule.modulestore
import
ModuleStoreEnum
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.tests.factories
import
ItemFactory
from
xmodule.modulestore.tests.factories
import
CourseFactory
,
ItemFactory
class
ContainerPageTestCase
(
StudioPageTestCase
):
class
ContainerPageTestCase
(
StudioPageTestCase
,
LibraryTestCase
):
"""
Unit tests for the container page.
"""
...
...
@@ -128,6 +130,44 @@ class ContainerPageTestCase(StudioPageTestCase):
self
.
validate_preview_html
(
published_child_container
,
self
.
container_view
)
self
.
validate_preview_html
(
published_child_vertical
,
self
.
reorderable_child_view
)
def
test_library_page_preview_html
(
self
):
"""
Verify that a library xblock's container (library page) preview returns the expected HTML.
"""
# Add some content to library.
self
.
_add_simple_content_block
()
self
.
validate_preview_html
(
self
.
library
,
self
.
container_view
,
can_reorder
=
False
,
can_move
=
False
)
def
test_library_content_preview_html
(
self
):
"""
Verify that a library content block container page preview returns the expected HTML.
"""
# Library content block is only supported in split courses.
with
modulestore
()
.
default_store
(
ModuleStoreEnum
.
Type
.
split
):
course
=
CourseFactory
.
create
()
# Add some content to library
self
.
_add_simple_content_block
()
# Create a library content block
lc_block
=
self
.
_add_library_content_block
(
course
,
self
.
lib_key
)
self
.
assertEqual
(
len
(
lc_block
.
children
),
0
)
# Refresh children to be reflected in lc_block
lc_block
=
self
.
_refresh_children
(
lc_block
)
self
.
assertEqual
(
len
(
lc_block
.
children
),
1
)
self
.
validate_preview_html
(
lc_block
,
self
.
container_view
,
can_add
=
False
,
can_reorder
=
False
,
can_move
=
False
,
can_edit
=
True
,
can_duplicate
=
False
,
can_delete
=
False
)
def
test_draft_container_preview_html
(
self
):
"""
Verify that a draft xblock's container preview returns the expected HTML.
...
...
cms/djangoapps/contentstore/views/tests/test_item.py
View file @
485ffb1b
This diff is collapsed.
Click to expand it.
cms/djangoapps/contentstore/views/tests/utils.py
View file @
485ffb1b
...
...
@@ -41,34 +41,48 @@ class StudioPageTestCase(CourseTestCase):
resp_content
=
json
.
loads
(
resp
.
content
)
return
resp_content
[
'html'
]
def
validate_preview_html
(
self
,
xblock
,
view_name
,
can_add
=
True
):
def
validate_preview_html
(
self
,
xblock
,
view_name
,
can_add
=
True
,
can_reorder
=
True
,
can_move
=
True
,
can_edit
=
True
,
can_duplicate
=
True
,
can_delete
=
True
):
"""
Verify that the specified xblock's preview has the expected HTML elements.
"""
html
=
self
.
get_preview_html
(
xblock
,
view_name
)
self
.
validate_html_for_add_buttons
(
html
,
can_add
)
# Verify drag handles always appear.
drag_handle_html
=
'<span data-tooltip="Drag to reorder" class="drag-handle action"></span>'
self
.
assertIn
(
drag_handle_html
,
html
)
# Verify that there are no action buttons for public blocks
expected_button_html
=
[
'<button class="btn-default edit-button action-button">'
,
self
.
validate_html_for_action_button
(
html
,
'<div class="add-xblock-component new-component-item adding"></div>'
,
can_add
)
self
.
validate_html_for_action_button
(
html
,
'<span data-tooltip="Drag to reorder" class="drag-handle action"></span>'
,
can_reorder
)
self
.
validate_html_for_action_button
(
html
,
'<button data-tooltip="Move" class="btn-default move-button action-button">'
,
can_move
)
self
.
validate_html_for_action_button
(
html
,
'button class="btn-default edit-button action-button">'
,
can_edit
)
self
.
validate_html_for_action_button
(
html
,
'<button data-tooltip="Delete" class="btn-default delete-button action-button">'
,
can_duplicate
)
self
.
validate_html_for_action_button
(
html
,
'<button data-tooltip="Duplicate" class="btn-default duplicate-button action-button">'
,
'<button data-tooltip="Move" class="btn-default move-button action-button">'
]
for
button_html
in
expected_button_html
:
self
.
assertIn
(
button_html
,
html
)
can_delete
)
def
validate_html_for_a
dd_buttons
(
self
,
html
,
can_add
=
True
):
def
validate_html_for_a
ction_button
(
self
,
html
,
expected_html
,
can_action
=
True
):
"""
Validate that the specified HTML has
the appropriate add actions for the current publish state
.
Validate that the specified HTML has
specific action.
.
"""
# Verify that there are no add buttons for public blocks
add_button_html
=
'<div class="add-xblock-component new-component-item adding"></div>'
if
can_add
:
self
.
assertIn
(
add_button_html
,
html
)
if
can_action
:
self
.
assertIn
(
expected_html
,
html
)
else
:
self
.
assertNotIn
(
add_button
_html
,
html
)
self
.
assertNotIn
(
expected
_html
,
html
)
cms/static/js/spec/views/move_xblock_spec.js
View file @
485ffb1b
...
...
@@ -205,13 +205,17 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
return
category
+
'_display_name_'
+
xblockIndex
;
})
);
if
(
category
!==
'component'
)
{
if
(
category
===
'component'
)
{
if
(
hasCurrentLocation
)
{
expect
(
displayedInfo
.
currentLocationText
).
toEqual
(
'(Currently selected)'
);
}
}
else
{
if
(
hasCurrentLocation
)
{
expect
(
displayedInfo
.
currentLocationText
).
toEqual
(
'(Current location)'
);
}
expect
(
displayedInfo
.
forwardButtonSRTexts
).
toEqual
(
_
.
map
(
_
.
range
(
expectedXBlocksCount
),
function
()
{
return
'
Click for children
'
;
return
'
View child items
'
;
})
);
expect
(
displayedInfo
.
forwardButtonCount
).
toEqual
(
expectedXBlocksCount
);
...
...
@@ -519,15 +523,8 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
});
});
describe
(
'Move an xblock'
,
function
()
{
it
(
'can not move in a disabled state'
,
function
()
{
verifyMoveEnabled
(
false
);
modal
.
$el
.
find
(
'.modal-actions .action-move'
).
click
();
expect
(
modal
.
movedAlertView
).
toBeNull
();
expect
(
getSentRequests
().
length
).
toEqual
(
0
);
});
it
(
'move button is disabled when navigating to same parent'
,
function
()
{
describe
(
'Move button'
,
function
()
{
it
(
'is disabled when navigating to same parent'
,
function
()
{
// select a target parent as the same as source parent and click
renderViews
(
courseOutline
);
_
.
each
(
_
.
range
(
3
),
function
()
{
...
...
@@ -536,7 +533,7 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
verifyMoveEnabled
(
'component'
,
true
);
});
it
(
'
move button
is enabled when navigating to different parent'
,
function
()
{
it
(
'is enabled when navigating to different parent'
,
function
()
{
// select a target parent as the different as source parent and click
renderViews
(
courseOutline
);
_
.
each
(
_
.
range
(
3
),
function
()
{
...
...
@@ -553,6 +550,111 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
verifyXBlockInfo
(
courseOutlineOptions
,
'section'
,
1
,
'forward'
,
false
);
});
it
(
'is disbabled when navigating to same source xblock'
,
function
()
{
var
outline
,
libraryContentXBlockInfo
=
{
category
:
'library_content'
,
display_name
:
'Library Content'
,
has_children
:
true
,
id
:
'LIBRARY_CONTENT_ID'
},
outlineOptions
=
{
library_content
:
1
,
component
:
1
};
// make above xblock source xblock.
modal
.
sourceXBlockInfo
=
libraryContentXBlockInfo
;
outline
=
createXBlockInfo
(
'component'
,
outlineOptions
,
libraryContentXBlockInfo
);
renderViews
(
outline
);
expect
(
modal
.
$el
.
find
(
'.modal-actions .action-move'
).
hasClass
(
'is-disabled'
)).
toBeTruthy
();
// select a target parent
clickForwardButton
(
0
);
expect
(
modal
.
$el
.
find
(
'.modal-actions .action-move'
).
hasClass
(
'is-disabled'
)).
toBeTruthy
();
});
it
(
'is disabled when navigating inside source content experiment'
,
function
()
{
var
outline
,
splitTestXBlockInfo
=
{
category
:
'split_test'
,
display_name
:
'Content Experiment'
,
has_children
:
true
,
id
:
'SPLIT_TEST_ID'
},
outlineOptions
=
{
split_test
:
1
,
unit
:
2
,
component
:
1
};
// make above xblock source xblock.
modal
.
sourceXBlockInfo
=
splitTestXBlockInfo
;
outline
=
createXBlockInfo
(
'unit'
,
outlineOptions
,
splitTestXBlockInfo
);
renderViews
(
outline
);
expect
(
modal
.
$el
.
find
(
'.modal-actions .action-move'
).
hasClass
(
'is-disabled'
)).
toBeTruthy
();
// navigate to groups level
clickForwardButton
(
0
);
expect
(
modal
.
$el
.
find
(
'.modal-actions .action-move'
).
hasClass
(
'is-disabled'
)).
toBeTruthy
();
// navigate to component level inside a group
clickForwardButton
(
0
);
// move should be disabled because we are navigating inside source xblock
expect
(
modal
.
$el
.
find
(
'.modal-actions .action-move'
).
hasClass
(
'is-disabled'
)).
toBeTruthy
();
});
it
(
'is disabled when navigating to any content experiment groups'
,
function
()
{
var
outline
,
splitTestXBlockInfo
=
{
category
:
'split_test'
,
display_name
:
'Content Experiment'
,
has_children
:
true
,
id
:
'SPLIT_TEST_ID'
},
outlineOptions
=
{
split_test
:
1
,
unit
:
2
,
component
:
1
};
// group level should be disabled but component level inside groups should be movable
outline
=
createXBlockInfo
(
'unit'
,
outlineOptions
,
splitTestXBlockInfo
);
renderViews
(
outline
);
// move is disabled on groups level
expect
(
modal
.
$el
.
find
(
'.modal-actions .action-move'
).
hasClass
(
'is-disabled'
)).
toBeTruthy
();
// navigate to component level inside a group
clickForwardButton
(
1
);
expect
(
modal
.
$el
.
find
(
'.modal-actions .action-move'
).
hasClass
(
'is-disabled'
)).
toBeFalsy
();
});
it
(
'is enabled when navigating to any parentable component'
,
function
()
{
var
parentableXBlockInfo
=
{
category
:
'vertical'
,
display_name
:
'Parentable Component'
,
has_children
:
true
,
id
:
'PARENTABLE_ID'
};
renderViews
(
parentableXBlockInfo
);
// move is enabled on parentable xblocks.
expect
(
modal
.
$el
.
find
(
'.modal-actions .action-move'
).
hasClass
(
'is-disabled'
)).
toBeFalsy
();
});
it
(
'is disabled when navigating to any non-parentable component'
,
function
()
{
var
nonParentableXBlockInfo
=
{
category
:
'html'
,
display_name
:
'Non Parentable Component'
,
has_children
:
false
,
id
:
'NON_PARENTABLE_ID'
};
renderViews
(
nonParentableXBlockInfo
);
// move is disabled on non-parent xblocks.
expect
(
modal
.
$el
.
find
(
'.modal-actions .action-move'
).
hasClass
(
'is-disabled'
)).
toBeTruthy
();
});
});
describe
(
'Move an xblock'
,
function
()
{
it
(
'can not move in a disabled state'
,
function
()
{
verifyMoveEnabled
(
false
);
modal
.
$el
.
find
(
'.modal-actions .action-move'
).
click
();
expect
(
modal
.
movedAlertView
).
toBeNull
();
expect
(
getSentRequests
().
length
).
toEqual
(
0
);
});
it
(
'move an xblock when move button is clicked'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
moveXBlockWithSuccess
(
requests
);
...
...
cms/static/js/views/modals/move_xblock_modal.js
View file @
485ffb1b
...
...
@@ -122,6 +122,7 @@ function($, Backbone, _, gettext, BaseView, XBlockViewUtils, MoveXBlockUtils, Ht
this
.
moveXBlockListView
=
new
MoveXBlockListView
(
{
model
:
new
XBlockInfoModel
(
courseOutlineInfo
,
{
parse
:
true
}),
sourceXBlockInfo
:
this
.
sourceXBlockInfo
,
ancestorInfo
:
ancestorInfo
}
);
...
...
@@ -136,12 +137,30 @@ function($, Backbone, _, gettext, BaseView, XBlockViewUtils, MoveXBlockUtils, Ht
}
},
isValidCategory
:
function
(
sourceParentType
,
targetParentType
,
targetHasChildren
)
{
var
basicBlockTypes
=
[
'course'
,
'chapter'
,
'sequential'
,
'vertical'
];
// Treat source parent component as vertical to support move child components under content experiment
// and other similar xblocks.
// eslint-disable-next-line no-param-reassign
sourceParentType
=
sourceParentType
===
'split_test'
?
'vertical'
:
sourceParentType
;
// Treat target parent component as a vertical to support move to parentable target parent components.
// Also, moving a component directly to content experiment is not allowed, we need to visit to group level.
if
(
targetHasChildren
&&
!
_
.
contains
(
basicBlockTypes
,
targetParentType
)
&&
targetParentType
!==
'split_test'
)
{
targetParentType
=
'vertical'
;
// eslint-disable-line no-param-reassign
}
return
targetParentType
===
sourceParentType
;
},
enableMoveOperation
:
function
(
targetParentXBlockInfo
)
{
var
isValidMove
=
false
,
sourceParentType
=
this
.
sourceParentXBlockInfo
.
get
(
'category'
),
targetParentType
=
targetParentXBlockInfo
.
get
(
'category'
);
targetParentType
=
targetParentXBlockInfo
.
get
(
'category'
),
targetHasChildren
=
targetParentXBlockInfo
.
get
(
'has_children'
);
if
(
targetParentType
===
sourceParentType
&&
this
.
sourceParentXBlockInfo
.
id
!==
targetParentXBlockInfo
.
id
)
{
if
(
this
.
isValidCategory
(
sourceParentType
,
targetParentType
,
targetHasChildren
)
&&
this
.
sourceParentXBlockInfo
.
id
!==
targetParentXBlockInfo
.
id
&&
// same parent case
this
.
sourceXBlockInfo
.
id
!==
targetParentXBlockInfo
.
id
)
{
// same source item case
isValidMove
=
true
;
this
.
targetParentXBlockInfo
=
targetParentXBlockInfo
;
}
...
...
cms/static/js/views/move_xblock_list.js
View file @
485ffb1b
...
...
@@ -33,7 +33,8 @@ function($, Backbone, _, gettext, HtmlUtils, StringUtils, XBlockUtils, MoveXBloc
section
:
gettext
(
'Sections'
),
subsection
:
gettext
(
'Subsections'
),
unit
:
gettext
(
'Units'
),
component
:
gettext
(
'Components'
)
component
:
gettext
(
'Components'
),
group
:
gettext
(
'Groups'
)
},
events
:
{
...
...
@@ -43,6 +44,7 @@ function($, Backbone, _, gettext, HtmlUtils, StringUtils, XBlockUtils, MoveXBloc
initialize
:
function
(
options
)
{
this
.
visitedAncestors
=
[];
this
.
template
=
HtmlUtils
.
template
(
MoveXBlockListViewTemplate
);
this
.
sourceXBlockInfo
=
options
.
sourceXBlockInfo
;
this
.
ancestorInfo
=
options
.
ancestorInfo
;
this
.
listenTo
(
Backbone
,
'move:breadcrumbButtonPressed'
,
this
.
handleBreadcrumbButtonPress
);
this
.
renderXBlockInfo
();
...
...
@@ -53,6 +55,7 @@ function($, Backbone, _, gettext, HtmlUtils, StringUtils, XBlockUtils, MoveXBloc
this
.
$el
,
this
.
template
(
{
sourceXBlockId
:
this
.
sourceXBlockInfo
.
id
,
xblocks
:
this
.
childrenInfo
.
children
,
noChildText
:
this
.
getNoChildText
(),
categoryText
:
this
.
getCategoryText
(),
...
...
@@ -123,10 +126,14 @@ function($, Backbone, _, gettext, HtmlUtils, StringUtils, XBlockUtils, MoveXBloc
* Set parent and child XBlock categories.
*/
setDisplayedXBlocksCategories
:
function
()
{
this
.
parentInfo
.
category
=
XBlockUtils
.
getXBlockType
(
this
.
parentInfo
.
parent
.
get
(
'category'
),
this
.
visitedAncestors
[
this
.
visitedAncestors
.
length
-
2
]
);
var
childCategory
=
'component'
;
this
.
parentInfo
.
category
=
XBlockUtils
.
getXBlockType
(
this
.
parentInfo
.
parent
.
get
(
'category'
));
if
(
!
_
.
contains
(
_
.
keys
(
this
.
categoryRelationMap
),
this
.
parentInfo
.
category
))
{
if
(
this
.
parentInfo
.
category
===
'split_test'
)
{
childCategory
=
'group'
;
// This is just to show groups text on group listing.
}
this
.
categoryRelationMap
[
this
.
parentInfo
.
category
]
=
childCategory
;
}
this
.
childrenInfo
.
category
=
this
.
categoryRelationMap
[
this
.
parentInfo
.
category
];
},
...
...
@@ -136,25 +143,19 @@ function($, Backbone, _, gettext, HtmlUtils, StringUtils, XBlockUtils, MoveXBloc
* @returns {any} Integer or undefined
*/
getCurrentLocationIndex
:
function
()
{
var
category
,
ancestorXBlock
,
currentLocationIndex
;
if
(
this
.
childrenInfo
.
category
===
'component'
||
this
.
childrenInfo
.
children
.
length
===
0
)
{
return
currentLocationIndex
;
}
category
=
this
.
childrenInfo
.
children
[
0
].
get
(
'category'
);
ancestorXBlock
=
_
.
find
(
this
.
ancestorInfo
.
ancestors
,
function
(
ancestor
)
{
return
ancestor
.
category
===
category
;
}
);
if
(
ancestorXBlock
)
{
_
.
each
(
this
.
childrenInfo
.
children
,
function
(
xblock
,
index
)
{
if
(
ancestorXBlock
.
display_name
===
xblock
.
get
(
'display_name'
)
&&
ancestorXBlock
.
id
===
xblock
.
get
(
'id'
))
{
currentLocationIndex
=
index
;
}
});
}
var
self
=
this
,
currentLocationIndex
;
_
.
each
(
self
.
childrenInfo
.
children
,
function
(
xblock
,
index
)
{
if
(
xblock
.
get
(
'id'
)
===
self
.
sourceXBlockInfo
.
id
)
{
currentLocationIndex
=
index
;
}
else
{
_
.
each
(
self
.
ancestorInfo
.
ancestors
,
function
(
ancestor
)
{
if
(
ancestor
.
display_name
===
xblock
.
get
(
'display_name'
)
&&
ancestor
.
id
===
xblock
.
get
(
'id'
))
{
currentLocationIndex
=
index
;
}
});
}
});
return
currentLocationIndex
;
},
...
...
cms/static/sass/views/_container.scss
View file @
485ffb1b
...
...
@@ -395,20 +395,21 @@
}
.component
{
display
:
block
;
display
:
inline-
block
;
color
:
$black
;
padding
:
(
$baseline
/
4
)
(
$baseline
/
2
);
}
.xblock-displayname
{
@include
float
(
left
);
}
.button-forward
,
.component
{
border
:
none
;
padding
:
(
$baseline
/
2
);
}
.button-forward
{
.xblock-displayname
{
@include
float
(
left
);
}
padding
:
(
$baseline
/
2
);
.forward-sr-icon
{
@include
float
(
right
);
...
...
cms/templates/js/move-xblock-list.underscore
View file @
485ffb1b
...
...
@@ -18,11 +18,7 @@
var xblock = xblocks[i];
%>
<li class="xblock-item" data-item-index="<%- i %>">
<% if (XBlocksCategory === 'component') { %>
<span class="xblock-displayname component truncate">
<%- xblock.get('display_name') %>
</span>
<% } else { %>
<% if (sourceXBlockId !== xblock.id && (xblock.get('child_info') || XBlocksCategory !== 'component')) { %>
<button class="button-forward" >
<span class="xblock-displayname truncate">
<%- xblock.get('display_name') %>
...
...
@@ -33,8 +29,19 @@
</span>
<% } %>
<span class="icon fa fa-arrow-right forward-sr-icon" aria-hidden="true"></span>
<span class="sr forward-sr-text"><%- gettext("
Click for children
") %></span>
<span class="sr forward-sr-text"><%- gettext("
View child items
") %></span>
</button>
<% } else { %>
<span class="component">
<span class="xblock-displayname truncate">
<%- xblock.get('display_name') %>
</span>
<% if(currentLocationIndex === i) { %>
<span class="current-location">
(<%- gettext('Currently selected') %>)
</span>
<% } %>
</span>
<% } %>
</li>
<% } %>
...
...
cms/templates/studio_xblock_wrapper.html
View file @
485ffb1b
...
...
@@ -89,7 +89,8 @@ messages = xblock.validate().to_json()
<span
class=
"sr"
>
${_("Duplicate")}
</span>
</button>
</li>
% endif
% if can_move:
<li
class=
"action-item action-move"
>
<button
data-tooltip=
"${_("
Move
")}"
class=
"btn-default move-button action-button"
>
<span
class=
"stack-move-icon fa-stack fa-lg "
>
...
...
common/lib/xmodule/xmodule/library_content_module.py
View file @
485ffb1b
...
...
@@ -338,6 +338,7 @@ class LibraryContentModule(LibraryContentFields, XModule, StudioEditableModule):
'display_name'
:
self
.
display_name
or
self
.
url_name
,
}))
context
[
'can_edit_visibility'
]
=
False
context
[
'can_move'
]
=
False
self
.
render_children
(
context
,
fragment
,
can_reorder
=
False
,
can_add
=
False
)
# else: When shown on a unit page, don't show any sort of preview -
# just the status of this block in the validation area.
...
...
common/lib/xmodule/xmodule/library_root_xblock.py
View file @
485ffb1b
...
...
@@ -80,6 +80,7 @@ class LibraryRoot(XBlock):
children_to_show
=
self
.
children
[
item_start
:
item_end
]
# pylint: disable=no-member
force_render
=
context
.
get
(
'force_render'
,
None
)
context
[
'can_move'
]
=
False
for
child_key
in
children_to_show
:
# Children must have a separate context from the library itself. Make a copy.
...
...
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