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
51905f0c
Commit
51905f0c
authored
Apr 01, 2015
by
Jonathan Piacenti
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Addressed notes for RCB duplication, removed management buttons from RCB container
parent
a6c00635
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
71 additions
and
40 deletions
+71
-40
cms/djangoapps/contentstore/views/item.py
+4
-5
cms/djangoapps/contentstore/views/preview.py
+2
-2
cms/templates/studio_xblock_wrapper.html
+15
-10
common/lib/xmodule/xmodule/library_content_module.py
+14
-20
common/lib/xmodule/xmodule/library_root_xblock.py
+1
-0
common/lib/xmodule/xmodule/library_tools.py
+2
-3
common/lib/xmodule/xmodule/studio_editable.py
+1
-0
common/test/acceptance/pages/studio/container.py
+14
-0
common/test/acceptance/tests/studio/test_studio_library_container.py
+18
-0
No files found.
cms/djangoapps/contentstore/views/item.py
View file @
51905f0c
...
@@ -593,18 +593,17 @@ def _duplicate_item(parent_usage_key, duplicate_source_usage_key, user, display_
...
@@ -593,18 +593,17 @@ def _duplicate_item(parent_usage_key, duplicate_source_usage_key, user, display_
runtime
=
source_item
.
runtime
,
runtime
=
source_item
.
runtime
,
)
)
handle_children
=
True
children_handled
=
False
handle_parenting
=
True
if
hasattr
(
dest_module
,
'studio_post_duplicate'
):
if
hasattr
(
dest_module
,
'studio_post_duplicate'
):
# Allow an XBlock to do anything fancy it may need to when duplicated from another block.
# Allow an XBlock to do anything fancy it may need to when duplicated from another block.
# These blocks may handle their own children or parenting if needed. Let them return booleans to
# These blocks may handle their own children or parenting if needed. Let them return booleans to
# let us know if we need to handle these or not.
# let us know if we need to handle these or not.
handle_children
,
handle_parenting
=
dest_module
.
studio_post_duplicate
(
store
,
parent_usage_key
,
source_item
)
children_handled
=
dest_module
.
studio_post_duplicate
(
store
,
source_item
)
# Children are not automatically copied over (and not all xblocks have a 'children' attribute).
# Children are not automatically copied over (and not all xblocks have a 'children' attribute).
# Because DAGs are not fully supported, we need to actually duplicate each child as well.
# Because DAGs are not fully supported, we need to actually duplicate each child as well.
if
source_item
.
has_children
and
handle_children
:
if
source_item
.
has_children
and
not
children_handled
:
dest_module
.
children
=
dest_module
.
children
or
[]
dest_module
.
children
=
dest_module
.
children
or
[]
for
child
in
source_item
.
children
:
for
child
in
source_item
.
children
:
dupe
=
_duplicate_item
(
dest_module
.
location
,
child
,
user
=
user
)
dupe
=
_duplicate_item
(
dest_module
.
location
,
child
,
user
=
user
)
...
@@ -613,7 +612,7 @@ def _duplicate_item(parent_usage_key, duplicate_source_usage_key, user, display_
...
@@ -613,7 +612,7 @@ def _duplicate_item(parent_usage_key, duplicate_source_usage_key, user, display_
store
.
update_item
(
dest_module
,
user
.
id
)
store
.
update_item
(
dest_module
,
user
.
id
)
# pylint: disable=protected-access
# pylint: disable=protected-access
if
(
'detached'
not
in
source_item
.
runtime
.
load_block_type
(
category
)
.
_class_tags
)
and
handle_parenting
:
if
(
'detached'
not
in
source_item
.
runtime
.
load_block_type
(
category
)
.
_class_tags
):
parent
=
store
.
get_item
(
parent_usage_key
)
parent
=
store
.
get_item
(
parent_usage_key
)
# If source was already a child of the parent, add duplicate immediately afterward.
# If source was already a child of the parent, add duplicate immediately afterward.
# Otherwise, add child to end.
# Otherwise, add child to end.
...
...
cms/djangoapps/contentstore/views/preview.py
View file @
51905f0c
...
@@ -240,7 +240,6 @@ def _studio_wrap_xblock(xblock, view, frag, context, display_name_only=False):
...
@@ -240,7 +240,6 @@ def _studio_wrap_xblock(xblock, view, frag, context, display_name_only=False):
# Only add the Studio wrapper when on the container page. The "Pages" page will remain as is for now.
# Only add the Studio wrapper when on the container page. The "Pages" page will remain as is for now.
if
not
context
.
get
(
'is_pages_view'
,
None
)
and
view
in
PREVIEW_VIEWS
:
if
not
context
.
get
(
'is_pages_view'
,
None
)
and
view
in
PREVIEW_VIEWS
:
root_xblock
=
context
.
get
(
'root_xblock'
)
root_xblock
=
context
.
get
(
'root_xblock'
)
can_edit_visibility
=
not
isinstance
(
xblock
.
location
,
LibraryUsageLocator
)
is_root
=
root_xblock
and
xblock
.
location
==
root_xblock
.
location
is_root
=
root_xblock
and
xblock
.
location
==
root_xblock
.
location
is_reorderable
=
_is_xblock_reorderable
(
xblock
,
context
)
is_reorderable
=
_is_xblock_reorderable
(
xblock
,
context
)
template_context
=
{
template_context
=
{
...
@@ -251,7 +250,8 @@ def _studio_wrap_xblock(xblock, view, frag, context, display_name_only=False):
...
@@ -251,7 +250,8 @@ def _studio_wrap_xblock(xblock, view, frag, context, display_name_only=False):
'is_root'
:
is_root
,
'is_root'
:
is_root
,
'is_reorderable'
:
is_reorderable
,
'is_reorderable'
:
is_reorderable
,
'can_edit'
:
context
.
get
(
'can_edit'
,
True
),
'can_edit'
:
context
.
get
(
'can_edit'
,
True
),
'can_edit_visibility'
:
can_edit_visibility
,
'can_edit_visibility'
:
context
.
get
(
'can_edit_visibility'
,
True
),
'can_add'
:
context
.
get
(
'can_add'
,
True
),
}
}
html
=
render_to_string
(
'studio_xblock_wrapper.html'
,
template_context
)
html
=
render_to_string
(
'studio_xblock_wrapper.html'
,
template_context
)
frag
=
wrap_fragment
(
frag
,
html
)
frag
=
wrap_fragment
(
frag
,
html
)
...
...
cms/templates/studio_xblock_wrapper.html
View file @
51905f0c
...
@@ -80,19 +80,24 @@ messages = json.dumps(xblock.validate().to_json())
...
@@ -80,19 +80,24 @@ messages = json.dumps(xblock.validate().to_json())
</a>
</a>
</li>
</li>
% endif
% endif
<li
class=
"action-item action-duplicate"
>
% if can_add:
<a
href=
"#"
data-tooltip=
"${_("
Duplicate
")}"
class=
"duplicate-button action-button"
>
<li
class=
"action-item action-duplicate"
>
<i
class=
"icon fa fa-copy"
></i>
<a
href=
"#"
data-tooltip=
"${_("
Duplicate
")}"
class=
"duplicate-button action-button"
>
<span
class=
"sr"
>
${_("Duplicate")}
</span>
<i
class=
"icon fa fa-copy"
></i>
<span
class=
"sr"
>
${_("Duplicate")}
</span>
</a>
</li>
% endif
% endif
% if can_add:
<!-- If we can add, we can delete. -->
<li
class=
"action-item action-delete"
>
<a
href=
"#"
data-tooltip=
"${_("
Delete
")}"
class=
"delete-button action-button"
>
<i
class=
"icon fa fa-trash-o"
></i>
<span
class=
"sr"
>
${_("Delete")}
</span>
</a>
</a>
</li>
</li>
% endif
% endif
<li
class=
"action-item action-delete"
>
<a
href=
"#"
data-tooltip=
"${_("
Delete
")}"
class=
"delete-button action-button"
>
<i
class=
"icon fa fa-trash-o"
></i>
<span
class=
"sr"
>
${_("Delete")}
</span>
</a>
</li>
% if is_reorderable:
% if is_reorderable:
<li
class=
"action-item action-drag"
>
<li
class=
"action-item action-drag"
>
<span
data-tooltip=
"${_('Drag to reorder')}"
class=
"drag-handle action"
></span>
<span
data-tooltip=
"${_('Drag to reorder')}"
class=
"drag-handle action"
></span>
...
...
common/lib/xmodule/xmodule/library_content_module.py
View file @
51905f0c
...
@@ -7,6 +7,7 @@ from lxml import etree
...
@@ -7,6 +7,7 @@ from lxml import etree
from
copy
import
copy
from
copy
import
copy
from
capa.responsetypes
import
registry
from
capa.responsetypes
import
registry
from
gettext
import
ngettext
from
gettext
import
ngettext
from
lazy
import
lazy
from
.mako_module
import
MakoModuleDescriptor
from
.mako_module
import
MakoModuleDescriptor
from
opaque_keys.edx.locator
import
LibraryLocator
from
opaque_keys.edx.locator
import
LibraryLocator
...
@@ -269,6 +270,7 @@ class LibraryContentModule(LibraryContentFields, XModule, StudioEditableModule):
...
@@ -269,6 +270,7 @@ class LibraryContentModule(LibraryContentFields, XModule, StudioEditableModule):
'max_count'
:
self
.
max_count
,
'max_count'
:
self
.
max_count
,
'display_name'
:
self
.
display_name
or
self
.
url_name
,
'display_name'
:
self
.
display_name
or
self
.
url_name
,
}))
}))
context
[
'can_edit_visibility'
]
=
False
self
.
render_children
(
context
,
fragment
,
can_reorder
=
False
,
can_add
=
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 -
# else: When shown on a unit page, don't show any sort of preview -
# just the status of this block in the validation area.
# just the status of this block in the validation area.
...
@@ -306,16 +308,12 @@ class LibraryContentDescriptor(LibraryContentFields, MakoModuleDescriptor, XmlDe
...
@@ -306,16 +308,12 @@ class LibraryContentDescriptor(LibraryContentFields, MakoModuleDescriptor, XmlDe
non_editable_fields
.
extend
([
LibraryContentFields
.
mode
,
LibraryContentFields
.
source_library_version
])
non_editable_fields
.
extend
([
LibraryContentFields
.
mode
,
LibraryContentFields
.
source_library_version
])
return
non_editable_fields
return
non_editable_fields
def
get_tools
(
self
):
@lazy
def
tools
(
self
):
"""
"""
Grab the library tools service or raise an error.
Grab the library tools service or raise an error.
"""
"""
lib_tools
=
self
.
runtime
.
service
(
self
,
'library_tools'
)
return
self
.
runtime
.
service
(
self
,
'library_tools'
)
if
not
lib_tools
:
# This error is diagnostic. The user won't see it, but it may be helpful
# during debugging.
return
Response
(
_
(
u"Course does not support Library tools."
),
status
=
400
)
return
lib_tools
def
get_user_id
(
self
):
def
get_user_id
(
self
):
"""
"""
...
@@ -343,14 +341,12 @@ class LibraryContentDescriptor(LibraryContentFields, MakoModuleDescriptor, XmlDe
...
@@ -343,14 +341,12 @@ class LibraryContentDescriptor(LibraryContentFields, MakoModuleDescriptor, XmlDe
the version number of the libraries used, so we easily determine if
the version number of the libraries used, so we easily determine if
this block is up to date or not.
this block is up to date or not.
"""
"""
lib_tools
=
self
.
get_tools
()
user_perms
=
self
.
runtime
.
service
(
self
,
'studio_user_permissions'
)
user_perms
=
self
.
runtime
.
service
(
self
,
'studio_user_permissions'
)
user_id
=
self
.
get_user_id
()
user_id
=
self
.
get_user_id
()
lib_
tools
.
update_children
(
self
,
user_id
,
user_perms
)
self
.
tools
.
update_children
(
self
,
user_id
,
user_perms
)
return
Response
()
return
Response
()
# pylint: disable=unused-argument
def
studio_post_duplicate
(
self
,
store
,
source_block
):
def
studio_post_duplicate
(
self
,
store
,
parent_usage_key
,
source_block
):
"""
"""
Used by the studio after basic duplication of a source block. We handle the children
Used by the studio after basic duplication of a source block. We handle the children
ourselves, because we have to properly reference the library upstream and set the overrides.
ourselves, because we have to properly reference the library upstream and set the overrides.
...
@@ -360,32 +356,30 @@ class LibraryContentDescriptor(LibraryContentFields, MakoModuleDescriptor, XmlDe
...
@@ -360,32 +356,30 @@ class LibraryContentDescriptor(LibraryContentFields, MakoModuleDescriptor, XmlDe
# The first task will be to refresh our copy of the library to generate the children.
# The first task will be to refresh our copy of the library to generate the children.
# We must do this at the currently set version of the library block. Otherwise we may not have
# We must do this at the currently set version of the library block. Otherwise we may not have
# exactly the same children-- someone may be duplicating an out of date block, after all.
# exactly the same children-- someone may be duplicating an out of date block, after all.
lib_tools
=
self
.
get_tools
()
user_id
=
self
.
get_user_id
()
user_id
=
self
.
get_user_id
()
user_perms
=
self
.
runtime
.
service
(
self
,
'studio_user_permissions'
)
user_perms
=
self
.
runtime
.
service
(
self
,
'studio_user_permissions'
)
# pylint: disable=no-member
# pylint: disable=no-member
lib_
tools
.
update_children
(
self
,
user_id
,
user_perms
,
version
=
self
.
source_library_version
)
self
.
tools
.
update_children
(
self
,
user_id
,
user_perms
,
version
=
self
.
source_library_version
)
# Copy over any overridden settings the course author may have applied to the blocks.
# Copy over any overridden settings the course author may have applied to the blocks.
def
copy_overrides
(
source
,
dest
):
def
copy_overrides
(
source
,
dest
):
"""
"""
Copy any overrides the user has made on blocks in this library.
Copy any overrides the user has made on blocks in this library.
"""
"""
for
field_name
in
source
.
fields
.
keys
():
for
field
in
source
.
fields
.
itervalues
():
field
=
dest
.
fields
[
field_name
]
if
field
.
scope
==
Scope
.
settings
and
field
.
is_set_on
(
source
):
if
field
.
scope
==
Scope
.
settings
and
field
.
is_set_on
(
source
):
setattr
(
dest
,
field
_name
,
getattr
(
source
,
field_nam
e
))
setattr
(
dest
,
field
.
name
,
field
.
read_from
(
sourc
e
))
if
source
.
has_children
:
if
source
.
has_children
:
source_children
=
[
s
tore
.
get_item
(
source_key
)
for
source_key
in
source
.
children
]
source_children
=
[
s
elf
.
runtime
.
get_block
(
source_key
)
for
source_key
in
source
.
children
]
dest_children
=
[
s
tore
.
get_item
(
dest_key
)
for
dest_key
in
dest
.
children
]
dest_children
=
[
s
elf
.
runtime
.
get_block
(
dest_key
)
for
dest_key
in
dest
.
children
]
for
source_child
,
dest_child
in
zip
(
source_children
,
dest_children
):
for
source_child
,
dest_child
in
zip
(
source_children
,
dest_children
):
copy_overrides
(
source_child
,
dest_child
)
copy_overrides
(
source_child
,
dest_child
)
store
.
update_item
(
dest
,
user_id
)
store
.
update_item
(
dest
,
user_id
)
copy_overrides
(
source_block
,
self
)
copy_overrides
(
source_block
,
self
)
#
Don't handle children. Handle parenting
.
#
Children have been handled
.
return
False
,
True
return
True
def
_validate_library_version
(
self
,
validation
,
lib_tools
,
version
,
library_key
):
def
_validate_library_version
(
self
,
validation
,
lib_tools
,
version
,
library_key
):
"""
"""
...
...
common/lib/xmodule/xmodule/library_root_xblock.py
View file @
51905f0c
...
@@ -82,6 +82,7 @@ class LibraryRoot(XBlock):
...
@@ -82,6 +82,7 @@ class LibraryRoot(XBlock):
# Children must have a separate context from the library itself. Make a copy.
# Children must have a separate context from the library itself. Make a copy.
child_context
=
context
.
copy
()
child_context
=
context
.
copy
()
child_context
[
'show_preview'
]
=
self
.
show_children_previews
child_context
[
'show_preview'
]
=
self
.
show_children_previews
child_context
[
'can_edit_visibility'
]
=
False
child
=
self
.
runtime
.
get_block
(
child_key
)
child
=
self
.
runtime
.
get_block
(
child_key
)
child_view_name
=
StudioEditableModule
.
get_preview_view_name
(
child
)
child_view_name
=
StudioEditableModule
.
get_preview_view_name
(
child
)
...
...
common/lib/xmodule/xmodule/library_tools.py
View file @
51905f0c
...
@@ -28,9 +28,8 @@ class LibraryToolsService(object):
...
@@ -28,9 +28,8 @@ class LibraryToolsService(object):
if
not
isinstance
(
library_key
,
LibraryLocator
):
if
not
isinstance
(
library_key
,
LibraryLocator
):
library_key
=
LibraryLocator
.
from_string
(
library_key
)
library_key
=
LibraryLocator
.
from_string
(
library_key
)
library_key
=
LibraryLocator
(
if
version
:
org
=
library_key
.
org
,
library
=
library_key
.
library
,
branch
=
library_key
.
branch
,
version_guid
=
version
library_key
.
for_version
(
version
)
)
try
:
try
:
return
self
.
store
.
get_library
(
library_key
,
remove_version
=
False
,
remove_branch
=
False
)
return
self
.
store
.
get_library
(
library_key
,
remove_version
=
False
,
remove_branch
=
False
)
...
...
common/lib/xmodule/xmodule/studio_editable.py
View file @
51905f0c
...
@@ -22,6 +22,7 @@ class StudioEditableBlock(object):
...
@@ -22,6 +22,7 @@ class StudioEditableBlock(object):
for
child
in
self
.
get_children
():
# pylint: disable=no-member
for
child
in
self
.
get_children
():
# pylint: disable=no-member
if
can_reorder
:
if
can_reorder
:
context
[
'reorderable_items'
]
.
add
(
child
.
location
)
context
[
'reorderable_items'
]
.
add
(
child
.
location
)
context
[
'can_add'
]
=
can_add
rendered_child
=
child
.
render
(
StudioEditableModule
.
get_preview_view_name
(
child
),
context
)
rendered_child
=
child
.
render
(
StudioEditableModule
.
get_preview_view_name
(
child
),
context
)
fragment
.
add_frag_resources
(
rendered_child
)
fragment
.
add_frag_resources
(
rendered_child
)
...
...
common/test/acceptance/pages/studio/container.py
View file @
51905f0c
...
@@ -407,6 +407,20 @@ class XBlockWrapper(PageObject):
...
@@ -407,6 +407,20 @@ class XBlockWrapper(PageObject):
return
self
.
q
(
css
=
self
.
_bounded_selector
(
'.wrapper-xblock.has-group-visibility-set'
))
.
is_present
()
return
self
.
q
(
css
=
self
.
_bounded_selector
(
'.wrapper-xblock.has-group-visibility-set'
))
.
is_present
()
@property
@property
def
has_duplicate_button
(
self
):
"""
Returns true if this xblock has a 'duplicate' button
"""
return
self
.
q
(
css
=
self
.
_bounded_selector
(
'a.duplicate-button'
))
@property
def
has_delete_button
(
self
):
"""
Returns true if this xblock has a 'delete' button
"""
return
self
.
q
(
css
=
self
.
_bounded_selector
(
'a.delete-button'
))
@property
def
has_edit_visibility_button
(
self
):
def
has_edit_visibility_button
(
self
):
"""
"""
Returns true if this xblock has an 'edit visibility' button
Returns true if this xblock has an 'edit visibility' button
...
...
common/test/acceptance/tests/studio/test_studio_library_container.py
View file @
51905f0c
...
@@ -290,3 +290,21 @@ class StudioLibraryContainerTest(StudioLibraryTest, UniqueCourseTest):
...
@@ -290,3 +290,21 @@ class StudioLibraryContainerTest(StudioLibraryTest, UniqueCourseTest):
block
.
reset_field_val
(
"Display Name"
)
block
.
reset_field_val
(
"Display Name"
)
block
.
save_settings
()
block
.
save_settings
()
self
.
assertEqual
(
block
.
name
,
name_default
)
self
.
assertEqual
(
block
.
name
,
name_default
)
def
test_cannot_manage
(
self
):
"""
Scenario: Given I have a library, a course and library content xblock in a course
When I go to studio unit page for library content block
And when I click the "View" link
Then I can see a preview of the blocks drawn from the library.
And I do not see a duplicate button
And I do not see a delete button
"""
block_wrapper_unit_page
=
self
.
_get_library_xblock_wrapper
(
self
.
unit_page
.
xblocks
[
0
]
.
children
[
0
])
container_page
=
block_wrapper_unit_page
.
go_to_container
()
for
block
in
container_page
.
xblocks
:
self
.
assertFalse
(
block
.
has_duplicate_button
)
self
.
assertFalse
(
block
.
has_delete_button
)
self
.
assertFalse
(
block
.
has_edit_visibility_button
)
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