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
b66a128c
Commit
b66a128c
authored
Mar 18, 2015
by
Kelketek
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #6939 from open-craft/content_libraries/previews
Add show/hide previews button to Content Libraries
parents
263dbdaf
568acb56
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
786 additions
and
97 deletions
+786
-97
cms/djangoapps/contentstore/views/component.py
+1
-0
cms/djangoapps/contentstore/views/item.py
+4
-1
cms/djangoapps/contentstore/views/library.py
+1
-0
cms/djangoapps/contentstore/views/preview.py
+7
-0
cms/static/js/spec/views/paged_container_spec.js
+112
-35
cms/static/js/spec/views/pages/container_spec.js
+49
-7
cms/static/js/views/paged_container.js
+71
-23
cms/static/js/views/pages/container.js
+10
-10
cms/static/js/views/pages/paged_container.js
+18
-3
cms/static/sass/elements/_xblocks.scss
+4
-0
cms/templates/js/mock/mock-container-page.underscore
+6
-0
cms/templates/js/mock/mock-paged-container-xblock.underscore
+261
-0
cms/templates/library.html
+9
-2
cms/templates/studio_xblock_wrapper.html
+16
-9
common/lib/xmodule/xmodule/library_root_xblock.py
+31
-5
common/test/acceptance/pages/studio/container.py
+6
-0
common/test/acceptance/pages/studio/library.py
+19
-0
common/test/acceptance/tests/studio/test_studio_library.py
+155
-1
lms/templates/studio_render_paged_children_view.html
+6
-1
No files found.
cms/djangoapps/contentstore/views/component.py
View file @
b66a128c
...
@@ -16,6 +16,7 @@ from xmodule.modulestore.django import modulestore
...
@@ -16,6 +16,7 @@ from xmodule.modulestore.django import modulestore
from
xblock.core
import
XBlock
from
xblock.core
import
XBlock
from
xblock.django.request
import
webob_to_django_response
,
django_to_webob_request
from
xblock.django.request
import
webob_to_django_response
,
django_to_webob_request
from
xblock.exceptions
import
NoSuchHandlerError
from
xblock.exceptions
import
NoSuchHandlerError
from
xblock.fields
import
Scope
from
xblock.plugin
import
PluginMissingError
from
xblock.plugin
import
PluginMissingError
from
xblock.runtime
import
Mixologist
from
xblock.runtime
import
Mixologist
...
...
cms/djangoapps/contentstore/views/item.py
View file @
b66a128c
...
@@ -264,7 +264,7 @@ def xblock_view_handler(request, usage_key_string, view_name):
...
@@ -264,7 +264,7 @@ def xblock_view_handler(request, usage_key_string, view_name):
# pylint: disable=too-many-format-args
# pylint: disable=too-many-format-args
return
HttpResponse
(
return
HttpResponse
(
content
=
"Couldn't parse paging parameters: enable_paging: "
content
=
"Couldn't parse paging parameters: enable_paging: "
"
%
s, page_number:
%
s, page_size:
%
s
"
.
format
(
"
{0}, page_number: {1}, page_size: {2}
"
.
format
(
request
.
REQUEST
.
get
(
'enable_paging'
,
'false'
),
request
.
REQUEST
.
get
(
'enable_paging'
,
'false'
),
request
.
REQUEST
.
get
(
'page_number'
,
0
),
request
.
REQUEST
.
get
(
'page_number'
,
0
),
request
.
REQUEST
.
get
(
'page_size'
,
0
)
request
.
REQUEST
.
get
(
'page_size'
,
0
)
...
@@ -273,6 +273,8 @@ def xblock_view_handler(request, usage_key_string, view_name):
...
@@ -273,6 +273,8 @@ def xblock_view_handler(request, usage_key_string, view_name):
content_type
=
"text/plain"
,
content_type
=
"text/plain"
,
)
)
force_render
=
request
.
REQUEST
.
get
(
'force_render'
,
None
)
# Set up the context to be passed to each XBlock's render method.
# Set up the context to be passed to each XBlock's render method.
context
=
{
context
=
{
'is_pages_view'
:
is_pages_view
,
# This setting disables the recursive wrapping of xblocks
'is_pages_view'
:
is_pages_view
,
# This setting disables the recursive wrapping of xblocks
...
@@ -281,6 +283,7 @@ def xblock_view_handler(request, usage_key_string, view_name):
...
@@ -281,6 +283,7 @@ def xblock_view_handler(request, usage_key_string, view_name):
'root_xblock'
:
xblock
if
(
view_name
==
'container_preview'
)
else
None
,
'root_xblock'
:
xblock
if
(
view_name
==
'container_preview'
)
else
None
,
'reorderable_items'
:
reorderable_items
,
'reorderable_items'
:
reorderable_items
,
'paging'
:
paging
,
'paging'
:
paging
,
'force_render'
:
force_render
,
}
}
fragment
=
get_preview_fragment
(
request
,
xblock
,
context
)
fragment
=
get_preview_fragment
(
request
,
xblock
,
context
)
...
...
cms/djangoapps/contentstore/views/library.py
View file @
b66a128c
...
@@ -231,4 +231,5 @@ def manage_library_users(request, library_key_string):
...
@@ -231,4 +231,5 @@ def manage_library_users(request, library_key_string):
'allow_actions'
:
bool
(
user_perms
&
STUDIO_EDIT_ROLES
),
'allow_actions'
:
bool
(
user_perms
&
STUDIO_EDIT_ROLES
),
'library_key'
:
unicode
(
library_key
),
'library_key'
:
unicode
(
library_key
),
'lib_users_url'
:
reverse_library_url
(
'manage_library_users'
,
library_key_string
),
'lib_users_url'
:
reverse_library_url
(
'manage_library_users'
,
library_key_string
),
'show_children_previews'
:
library
.
show_children_previews
})
})
cms/djangoapps/contentstore/views/preview.py
View file @
b66a128c
...
@@ -113,6 +113,12 @@ class PreviewModuleSystem(ModuleSystem): # pylint: disable=abstract-method
...
@@ -113,6 +113,12 @@ class PreviewModuleSystem(ModuleSystem): # pylint: disable=abstract-method
if
aside_type
!=
'acid_aside'
if
aside_type
!=
'acid_aside'
]
]
def
render_child_placeholder
(
self
,
block
,
view_name
,
context
):
"""
Renders a placeholder XBlock.
"""
return
self
.
wrap_xblock
(
block
,
view_name
,
Fragment
(),
context
)
class
StudioPermissionsService
(
object
):
class
StudioPermissionsService
(
object
):
"""
"""
...
@@ -240,6 +246,7 @@ def _studio_wrap_xblock(xblock, view, frag, context, display_name_only=False):
...
@@ -240,6 +246,7 @@ def _studio_wrap_xblock(xblock, view, frag, context, display_name_only=False):
template_context
=
{
template_context
=
{
'xblock_context'
:
context
,
'xblock_context'
:
context
,
'xblock'
:
xblock
,
'xblock'
:
xblock
,
'show_preview'
:
context
.
get
(
'show_preview'
,
True
),
'content'
:
frag
.
content
,
'content'
:
frag
.
content
,
'is_root'
:
is_root
,
'is_root'
:
is_root
,
'is_reorderable'
:
is_reorderable
,
'is_reorderable'
:
is_reorderable
,
...
...
cms/static/js/spec/views/paged_container_spec.js
View file @
b66a128c
define
([
"jquery"
,
"underscore"
,
"js/common_helpers/ajax_helpers"
,
"URI"
,
"js/models/xblock_info"
,
define
([
"jquery"
,
"underscore"
,
"js/common_helpers/ajax_helpers"
,
"URI"
,
"js/models/xblock_info"
,
"js/views/paged_container"
,
"js/views/paging_header"
,
"js/views/paging_footer"
],
"js/views/paged_container"
,
"js/views/paging_header"
,
"js/views/paging_footer"
,
"js/views/xblock"
],
function
(
$
,
_
,
AjaxHelpers
,
URI
,
XBlockInfo
,
PagedContainer
,
PagingHeader
,
PagingFooter
)
{
function
(
$
,
_
,
AjaxHelpers
,
URI
,
XBlockInfo
,
PagedContainer
,
PagingHeader
,
PagingFooter
,
XBlockView
)
{
var
htmlResponseTpl
=
_
.
template
(
''
+
var
htmlResponseTpl
=
_
.
template
(
''
+
'<div class="xblock-container-paging-parameters" data-start="<%= start %>" data-displayed="<%= displayed %>" data-total="<%= total %>"/>'
'<div class="xblock-container-paging-parameters" '
+
'data-start="<%= start %>" '
+
'data-displayed="<%= displayed %>" '
+
'data-total="<%= total %>" '
+
'data-previews="<%= previews %>"></div>'
);
);
function
getResponseHtml
(
options
){
function
getResponseHtml
(
override_options
){
var
default_options
=
{
start
:
0
,
displayed
:
PAGE_SIZE
,
total
:
PAGE_SIZE
+
1
,
previews
:
true
};
var
options
=
_
.
extend
(
default_options
,
override_options
);
return
'<div class="xblock" data-request-token="request_token">'
+
return
'<div class="xblock" data-request-token="request_token">'
+
'<div class="container-paging-header"></div>'
+
'<div class="container-paging-header"></div>'
+
htmlResponseTpl
(
options
)
+
htmlResponseTpl
(
options
)
+
...
@@ -14,43 +25,43 @@ define([ "jquery", "underscore", "js/common_helpers/ajax_helpers", "URI", "js/mo
...
@@ -14,43 +25,43 @@ define([ "jquery", "underscore", "js/common_helpers/ajax_helpers", "URI", "js/mo
'</div>'
'</div>'
}
}
var
makePage
=
function
(
html_parameters
)
{
return
{
resources
:
[],
html
:
getResponseHtml
(
html_parameters
)
};
};
var
PAGE_SIZE
=
3
;
var
PAGE_SIZE
=
3
;
var
mockFirstPage
=
{
var
mockFirstPage
=
makePage
({
resources
:
[],
html
:
getResponseHtml
({
start
:
0
,
start
:
0
,
displayed
:
PAGE_SIZE
,
displayed
:
PAGE_SIZE
,
total
:
PAGE_SIZE
+
1
total
:
PAGE_SIZE
+
1
})
});
};
var
mockSecondPage
=
{
var
mockSecondPage
=
makePage
({
resources
:
[],
start
:
PAGE_SIZE
,
html
:
getResponseHtml
({
displayed
:
1
,
start
:
PAGE_SIZE
,
total
:
PAGE_SIZE
+
1
displayed
:
1
,
});
total
:
PAGE_SIZE
+
1
})
};
var
mockEmptyPage
=
{
var
mockEmptyPage
=
makePage
({
resources
:
[],
start
:
0
,
html
:
getResponseHtml
({
displayed
:
0
,
start
:
0
,
total
:
0
displayed
:
0
,
});
total
:
0
})
};
var
respondWithMockPage
=
function
(
requests
)
{
var
respondWithMockPage
=
function
(
requests
,
mockPage
)
{
var
requestIndex
=
requests
.
length
-
1
;
var
requestIndex
=
requests
.
length
-
1
;
var
request
=
requests
[
requestIndex
];
if
(
typeof
mockPage
==
'undefined'
)
{
var
url
=
new
URI
(
request
.
url
);
var
request
=
requests
[
requestIndex
];
var
queryParameters
=
url
.
query
(
true
);
// Returns an object with each query parameter stored as a value
var
url
=
new
URI
(
request
.
url
);
var
page
=
queryParameters
.
page_number
;
var
queryParameters
=
url
.
query
(
true
);
// Returns an object with each query parameter stored as a value
var
response
=
page
===
"0"
?
mockFirstPage
:
mockSecondPage
;
var
page
=
queryParameters
.
page_number
;
AjaxHelpers
.
respondWithJson
(
requests
,
response
,
requestIndex
);
mockPage
=
page
===
"0"
?
mockFirstPage
:
mockSecondPage
;
}
AjaxHelpers
.
respondWithJson
(
requests
,
mockPage
,
requestIndex
);
};
};
var
MockPagingView
=
PagedContainer
.
extend
({
var
MockPagingView
=
PagedContainer
.
extend
({
...
@@ -65,10 +76,26 @@ define([ "jquery", "underscore", "js/common_helpers/ajax_helpers", "URI", "js/mo
...
@@ -65,10 +76,26 @@ define([ "jquery", "underscore", "js/common_helpers/ajax_helpers", "URI", "js/mo
beforeEach
(
function
()
{
beforeEach
(
function
()
{
var
feedbackTpl
=
readFixtures
(
'system-feedback.underscore'
);
var
feedbackTpl
=
readFixtures
(
'system-feedback.underscore'
);
setFixtures
(
$
(
"<script>"
,
{
id
:
"system-feedback-tpl"
,
type
:
"text/template"
}).
text
(
feedbackTpl
));
setFixtures
(
$
(
"<script>"
,
{
id
:
"system-feedback-tpl"
,
type
:
"text/template"
}).
text
(
feedbackTpl
));
pagingContainer
=
new
MockPagingView
({
page_size
:
PAGE_SIZE
});
pagingContainer
=
new
MockPagingView
({
page_size
:
PAGE_SIZE
});
});
});
describe
(
"Container"
,
function
()
{
describe
(
"Container"
,
function
()
{
describe
(
"rendering"
,
function
(){
it
(
'should set show_previews'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
expect
(
pagingContainer
.
collection
.
showChildrenPreviews
).
toBe
(
true
);
//precondition check
pagingContainer
.
setPage
(
0
);
respondWithMockPage
(
requests
,
makePage
({
previews
:
false
}));
expect
(
pagingContainer
.
collection
.
showChildrenPreviews
).
toBe
(
false
);
pagingContainer
.
setPage
(
0
);
respondWithMockPage
(
requests
,
makePage
({
previews
:
true
}));
expect
(
pagingContainer
.
collection
.
showChildrenPreviews
).
toBe
(
true
);
});
});
describe
(
"setPage"
,
function
()
{
describe
(
"setPage"
,
function
()
{
it
(
'can set the current page'
,
function
()
{
it
(
'can set the current page'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
var
requests
=
AjaxHelpers
.
requests
(
this
);
...
@@ -304,8 +331,6 @@ define([ "jquery", "underscore", "js/common_helpers/ajax_helpers", "URI", "js/mo
...
@@ -304,8 +331,6 @@ define([ "jquery", "underscore", "js/common_helpers/ajax_helpers", "URI", "js/mo
});
});
describe
(
"PagingFooter"
,
function
()
{
describe
(
"PagingFooter"
,
function
()
{
var
pagingFooter
;
beforeEach
(
function
()
{
beforeEach
(
function
()
{
var
pagingFooterTpl
=
readFixtures
(
'paging-footer.underscore'
);
var
pagingFooterTpl
=
readFixtures
(
'paging-footer.underscore'
);
appendSetFixtures
(
$
(
"<script>"
,
{
id
:
"paging-footer-tpl"
,
type
:
"text/template"
}).
text
(
pagingFooterTpl
));
appendSetFixtures
(
$
(
"<script>"
,
{
id
:
"paging-footer-tpl"
,
type
:
"text/template"
}).
text
(
pagingFooterTpl
));
...
@@ -485,5 +510,57 @@ define([ "jquery", "underscore", "js/common_helpers/ajax_helpers", "URI", "js/mo
...
@@ -485,5 +510,57 @@ define([ "jquery", "underscore", "js/common_helpers/ajax_helpers", "URI", "js/mo
});
});
});
});
});
});
describe
(
"Previews"
,
function
(){
describe
(
"Toggle Previews"
,
function
(){
var
testSendsAjax
,
defaultUrl
=
"/preview/xblock/handler/trigger_previews"
;
testSendsAjax
=
function
(
show_previews
)
{
it
(
"should send "
+
(
!
show_previews
)
+
" when showChildrenPreviews was "
+
show_previews
,
function
(){
var
requests
=
AjaxHelpers
.
requests
(
this
);
pagingContainer
.
collection
.
showChildrenPreviews
=
show_previews
;
pagingContainer
.
togglePreviews
();
AjaxHelpers
.
expectJsonRequest
(
requests
,
'POST'
,
defaultUrl
,
{
showChildrenPreviews
:
!
show_previews
});
AjaxHelpers
.
respondWithJson
(
requests
,
{
showChildrenPreviews
:
!
show_previews
});
});
};
testSendsAjax
(
true
);
testSendsAjax
(
false
);
it
(
"should trigger render on success"
,
function
(){
spyOn
(
pagingContainer
,
'render'
);
var
requests
=
AjaxHelpers
.
requests
(
this
);
pagingContainer
.
togglePreviews
();
AjaxHelpers
.
respondWithJson
(
requests
,
{
showChildrenPreviews
:
true
});
expect
(
pagingContainer
.
render
).
toHaveBeenCalled
();
});
it
(
"should not trigger render on failure"
,
function
(){
spyOn
(
pagingContainer
,
'render'
);
var
requests
=
AjaxHelpers
.
requests
(
this
);
pagingContainer
.
togglePreviews
();
AjaxHelpers
.
respondWithError
(
requests
);
expect
(
pagingContainer
.
render
).
not
.
toHaveBeenCalled
();
});
it
(
"should send force_render when new block causes page change"
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
pagingContainer
.
setPage
(
0
);
respondWithMockPage
(
requests
);
spyOn
(
pagingContainer
,
'render'
);
var
mockXBlockInfo
=
new
XBlockInfo
({
id
:
'mock-location'
});
var
mockXBlockView
=
new
XBlockView
({
model
:
mockXBlockInfo
});
mockXBlockView
.
model
.
id
=
'mock-location'
;
pagingContainer
.
refresh
(
mockXBlockView
,
true
);
expect
(
pagingContainer
.
render
).
toHaveBeenCalled
();
expect
(
pagingContainer
.
render
.
mostRecentCall
.
args
[
0
].
force_render
).
toEqual
(
'mock-location'
);
});
});
});
});
});
});
});
cms/static/js/spec/views/pages/container_spec.js
View file @
b66a128c
...
@@ -16,6 +16,7 @@ define(["jquery", "underscore", "underscore.string", "js/common_helpers/ajax_hel
...
@@ -16,6 +16,7 @@ define(["jquery", "underscore", "underscore.string", "js/common_helpers/ajax_hel
mockXBlockEditorHtml
=
readFixtures
(
'mock/mock-xblock-editor.underscore'
),
mockXBlockEditorHtml
=
readFixtures
(
'mock/mock-xblock-editor.underscore'
),
mockXBlockVisibilityEditorHtml
=
readFixtures
(
'mock/mock-xblock-visibility-editor.underscore'
),
mockXBlockVisibilityEditorHtml
=
readFixtures
(
'mock/mock-xblock-visibility-editor.underscore'
),
PageClass
=
fixtures
.
page
,
PageClass
=
fixtures
.
page
,
pagedSpecificTests
=
fixtures
.
paged_specific_tests
,
hasVisibilityEditor
=
fixtures
.
has_visibility_editor
;
hasVisibilityEditor
=
fixtures
.
has_visibility_editor
;
beforeEach
(
function
()
{
beforeEach
(
function
()
{
...
@@ -305,13 +306,9 @@ define(["jquery", "underscore", "underscore.string", "js/common_helpers/ajax_hel
...
@@ -305,13 +306,9 @@ define(["jquery", "underscore", "underscore.string", "js/common_helpers/ajax_hel
}
}
);
);
paginated
=
function
()
{
return
containerPage
instanceof
PagedContainerPage
;
};
getDeleteOffset
=
function
()
{
getDeleteOffset
=
function
()
{
// Paginated containers will make an additional AJAX request.
// Paginated containers will make an additional AJAX request.
return
pag
inated
()
?
3
:
2
;
return
pag
edSpecificTests
?
3
:
2
;
};
};
getGroupElement
=
function
()
{
getGroupElement
=
function
()
{
...
@@ -509,6 +506,48 @@ define(["jquery", "underscore", "underscore.string", "js/common_helpers/ajax_hel
...
@@ -509,6 +506,48 @@ define(["jquery", "underscore", "underscore.string", "js/common_helpers/ajax_hel
});
});
});
});
describe
(
"Previews"
,
function
()
{
var
getButtonIcon
,
getButtonText
;
getButtonIcon
=
function
(
containerPage
)
{
return
containerPage
.
$
(
'.action-toggle-preview i'
);
};
getButtonText
=
function
(
containerPage
)
{
return
containerPage
.
$
(
'.action-toggle-preview .preview-text'
).
text
().
trim
();
};
if
(
pagedSpecificTests
)
{
it
(
'has no text on the preview button to start with'
,
function
()
{
containerPage
=
getContainerPage
();
expect
(
getButtonIcon
(
containerPage
)).
toHaveClass
(
'fa-refresh'
);
expect
(
getButtonIcon
(
containerPage
).
parent
()).
toHaveClass
(
'is-hidden'
);
expect
(
getButtonText
(
containerPage
)).
toBe
(
""
);
});
function
updatePreviewButtonTest
(
show_previews
,
expected_text
)
{
it
(
'can set preview button to "'
+
expected_text
+
'"'
,
function
()
{
containerPage
=
getContainerPage
();
containerPage
.
updatePreviewButton
(
show_previews
);
expect
(
getButtonText
(
containerPage
)).
toBe
(
expected_text
);
});
}
updatePreviewButtonTest
(
true
,
'Hide Previews'
);
updatePreviewButtonTest
(
false
,
'Show Previews'
);
it
(
'triggers underlying view togglePreviews when preview button clicked'
,
function
()
{
containerPage
=
getContainerPage
();
containerPage
.
render
();
spyOn
(
containerPage
.
xblockView
,
'togglePreviews'
);
containerPage
.
$
(
'.toggle-preview-button'
).
click
();
expect
(
containerPage
.
xblockView
.
togglePreviews
).
toHaveBeenCalled
();
});
}
});
describe
(
'createNewComponent '
,
function
()
{
describe
(
'createNewComponent '
,
function
()
{
var
clickNewComponent
;
var
clickNewComponent
;
...
@@ -591,6 +630,7 @@ define(["jquery", "underscore", "underscore.string", "js/common_helpers/ajax_hel
...
@@ -591,6 +630,7 @@ define(["jquery", "underscore", "underscore.string", "js/common_helpers/ajax_hel
});
});
});
});
});
});
});
});
}
}
...
@@ -601,7 +641,8 @@ define(["jquery", "underscore", "underscore.string", "js/common_helpers/ajax_hel
...
@@ -601,7 +641,8 @@ define(["jquery", "underscore", "underscore.string", "js/common_helpers/ajax_hel
page
:
ContainerPage
,
page
:
ContainerPage
,
initial
:
'mock/mock-container-xblock.underscore'
,
initial
:
'mock/mock-container-xblock.underscore'
,
add_response
:
'mock/mock-xblock.underscore'
,
add_response
:
'mock/mock-xblock.underscore'
,
has_visibility_editor
:
true
has_visibility_editor
:
true
,
paged_specific_tests
:
false
}
}
);
);
...
@@ -612,7 +653,8 @@ define(["jquery", "underscore", "underscore.string", "js/common_helpers/ajax_hel
...
@@ -612,7 +653,8 @@ define(["jquery", "underscore", "underscore.string", "js/common_helpers/ajax_hel
page
:
PagedContainerPage
,
page
:
PagedContainerPage
,
initial
:
'mock/mock-container-paged-xblock.underscore'
,
initial
:
'mock/mock-container-paged-xblock.underscore'
,
add_response
:
'mock/mock-xblock-paged.underscore'
,
add_response
:
'mock/mock-xblock-paged.underscore'
,
has_visibility_editor
:
false
has_visibility_editor
:
false
,
paged_specific_tests
:
true
}
}
);
);
});
});
cms/static/js/views/paged_container.js
View file @
b66a128c
define
([
"jquery"
,
"underscore"
,
"js/views/container"
,
"js/utils/module"
,
"gettext"
,
define
([
"jquery"
,
"underscore"
,
"js/views/
utils/view_utils"
,
"js/views/
container"
,
"js/utils/module"
,
"gettext"
,
"js/views/feedback_notification"
,
"js/views/paging_header"
,
"js/views/paging_footer"
,
"js/views/paging_mixin"
],
"js/views/feedback_notification"
,
"js/views/paging_header"
,
"js/views/paging_footer"
,
"js/views/paging_mixin"
],
function
(
$
,
_
,
ContainerView
,
ModuleUtils
,
gettext
,
NotificationView
,
PagingHeader
,
PagingFooter
,
PagingMixin
)
{
function
(
$
,
_
,
ViewUtils
,
ContainerView
,
ModuleUtils
,
gettext
,
NotificationView
,
PagingHeader
,
PagingFooter
,
PagingMixin
)
{
var
PagedContainerView
=
ContainerView
.
extend
(
PagingMixin
).
extend
({
var
PagedContainerView
=
ContainerView
.
extend
(
PagingMixin
).
extend
({
initialize
:
function
(
options
){
initialize
:
function
(
options
){
var
self
=
this
;
var
self
=
this
;
...
@@ -24,7 +24,9 @@ define(["jquery", "underscore", "js/views/container", "js/utils/module", "gettex
...
@@ -24,7 +24,9 @@ define(["jquery", "underscore", "js/views/container", "js/utils/module", "gettex
bind
:
function
()
{},
bind
:
function
()
{},
// size() on backbone collections shows how many objects are in the collection, or in the case
// size() on backbone collections shows how many objects are in the collection, or in the case
// of paginator, on the current page.
// of paginator, on the current page.
size
:
function
()
{
return
self
.
collection
.
_size
;
}
size
:
function
()
{
return
self
.
collection
.
_size
;
},
// Toggles the functionality for showing and hiding child previews.
showChildrenPreviews
:
true
};
};
},
},
...
@@ -47,21 +49,29 @@ define(["jquery", "underscore", "js/views/container", "js/utils/module", "gettex
...
@@ -47,21 +49,29 @@ define(["jquery", "underscore", "js/views/container", "js/utils/module", "gettex
url
:
decodeURIComponent
(
xblockUrl
)
+
"/"
+
view
,
url
:
decodeURIComponent
(
xblockUrl
)
+
"/"
+
view
,
type
:
'GET'
,
type
:
'GET'
,
cache
:
false
,
cache
:
false
,
data
:
this
.
getRenderParameters
(
options
.
page_number
),
data
:
this
.
getRenderParameters
(
options
.
page_number
,
options
.
force_render
),
headers
:
{
Accept
:
'application/json'
},
headers
:
{
Accept
:
'application/json'
},
success
:
function
(
fragment
)
{
success
:
function
(
fragment
)
{
self
.
handleXBlockFragment
(
fragment
,
options
);
self
.
handleXBlockFragment
(
fragment
,
options
);
self
.
processPaging
({
requested_page
:
options
.
page_number
});
self
.
processPaging
({
requested_page
:
options
.
page_number
});
self
.
page
.
updatePreviewButton
(
self
.
collection
.
showChildrenPreviews
);
self
.
page
.
renderAddXBlockComponents
();
self
.
page
.
renderAddXBlockComponents
();
if
(
options
.
force_render
)
{
var
target
=
$
(
'.studio-xblock-wrapper[data-locator="'
+
options
.
force_render
+
'"]'
);
// Scroll us to the element with a little buffer at the top for context.
ViewUtils
.
setScrollOffset
(
target
,
(
$
(
window
).
height
()
*
.
10
));
}
}
}
});
});
},
},
getRenderParameters
:
function
(
page_number
)
{
getRenderParameters
:
function
(
page_number
,
force_render
)
{
// Options should at least contain page_number.
return
{
return
{
page_size
:
this
.
page_size
,
page_size
:
this
.
page_size
,
enable_paging
:
true
,
enable_paging
:
true
,
page_number
:
page_number
page_number
:
page_number
,
force_render
:
force_render
};
};
},
},
...
@@ -70,8 +80,10 @@ define(["jquery", "underscore", "js/views/container", "js/utils/module", "gettex
...
@@ -70,8 +80,10 @@ define(["jquery", "underscore", "js/views/container", "js/utils/module", "gettex
return
Math
.
ceil
(
total_count
/
this
.
page_size
);
return
Math
.
ceil
(
total_count
/
this
.
page_size
);
},
},
setPage
:
function
(
page_number
)
{
setPage
:
function
(
page_number
,
additional_options
)
{
this
.
render
({
page_number
:
page_number
});
additional_options
=
additional_options
||
{};
var
options
=
_
.
extend
({
page_number
:
page_number
},
additional_options
);
this
.
render
(
options
);
},
},
processPaging
:
function
(
options
){
processPaging
:
function
(
options
){
...
@@ -80,13 +92,15 @@ define(["jquery", "underscore", "js/views/container", "js/utils/module", "gettex
...
@@ -80,13 +92,15 @@ define(["jquery", "underscore", "js/views/container", "js/utils/module", "gettex
var
$element
=
this
.
$el
.
find
(
'.xblock-container-paging-parameters'
),
var
$element
=
this
.
$el
.
find
(
'.xblock-container-paging-parameters'
),
total
=
$element
.
data
(
'total'
),
total
=
$element
.
data
(
'total'
),
displayed
=
$element
.
data
(
'displayed'
),
displayed
=
$element
.
data
(
'displayed'
),
start
=
$element
.
data
(
'start'
);
start
=
$element
.
data
(
'start'
),
previews
=
$element
.
data
(
'previews'
);
this
.
collection
.
currentPage
=
options
.
requested_page
;
this
.
collection
.
currentPage
=
options
.
requested_page
;
this
.
collection
.
totalCount
=
total
;
this
.
collection
.
totalCount
=
total
;
this
.
collection
.
totalPages
=
this
.
getPageCount
(
total
);
this
.
collection
.
totalPages
=
this
.
getPageCount
(
total
);
this
.
collection
.
start
=
start
;
this
.
collection
.
start
=
start
;
this
.
collection
.
_size
=
displayed
;
this
.
collection
.
_size
=
displayed
;
this
.
collection
.
showChildrenPreviews
=
previews
;
this
.
processPagingHeaderAndFooter
();
this
.
processPagingHeaderAndFooter
();
},
},
...
@@ -112,23 +126,44 @@ define(["jquery", "underscore", "js/views/container", "js/utils/module", "gettex
...
@@ -112,23 +126,44 @@ define(["jquery", "underscore", "js/views/container", "js/utils/module", "gettex
this
.
pagingFooter
.
render
();
this
.
pagingFooter
.
render
();
},
},
refresh
:
function
(
block_added
)
{
refresh
:
function
(
xblockView
,
block_added
,
is_duplicate
)
{
if
(
block_added
)
{
if
(
!
block_added
)
{
this
.
collection
.
totalCount
+=
1
;
return
this
.
collection
.
_size
+=
1
;
}
if
(
this
.
collection
.
totalCount
==
1
)
{
if
(
is_duplicate
)
{
this
.
render
();
// Duplicated blocks can be inserted onto the current page.
var
xblock
=
xblockView
.
xblock
.
element
.
parents
(
".studio-xblock-wrapper"
).
first
();
var
all_xblocks
=
xblock
.
parent
().
children
(
".studio-xblock-wrapper"
);
var
index
=
all_xblocks
.
index
(
xblock
);
if
((
index
+
1
<=
this
.
page_size
)
&&
(
all_xblocks
.
length
>
this
.
page_size
))
{
// Pop the last XBlock off the bottom.
all_xblocks
[
all_xblocks
.
length
-
1
].
remove
();
return
return
}
}
this
.
collection
.
totalPages
=
this
.
getPageCount
(
this
.
collection
.
totalCount
);
}
var
new_page
=
this
.
collection
.
totalPages
-
1
;
this
.
collection
.
totalCount
+=
1
;
// If we're on a new page due to overflow, or this is the first item, set the page.
this
.
collection
.
_size
+=
1
;
if
(((
this
.
collection
.
currentPage
)
!=
new_page
)
||
this
.
collection
.
totalCount
==
1
)
{
if
(
this
.
collection
.
totalCount
==
1
)
{
this
.
setPage
(
new_page
);
this
.
render
();
}
else
{
return
this
.
pagingHeader
.
render
();
}
this
.
pagingFooter
.
render
();
this
.
collection
.
totalPages
=
this
.
getPageCount
(
this
.
collection
.
totalCount
);
var
target_page
=
this
.
collection
.
totalPages
-
1
;
// If we're on a new page due to overflow, or this is the first item, set the page.
if
(((
this
.
collection
.
currentPage
)
!=
target_page
)
||
this
.
collection
.
totalCount
==
1
)
{
var
force_render
=
xblockView
.
model
.
id
;
if
(
is_duplicate
)
{
// The duplicate should be on the next page if we've gotten here.
target_page
=
this
.
collection
.
currentPage
+
1
;
}
}
this
.
setPage
(
target_page
,
{
force_render
:
force_render
}
);
}
else
{
this
.
pagingHeader
.
render
();
this
.
pagingFooter
.
render
();
}
}
},
},
...
@@ -157,6 +192,19 @@ define(["jquery", "underscore", "js/views/container", "js/utils/module", "gettex
...
@@ -157,6 +192,19 @@ define(["jquery", "underscore", "js/views/container", "js/utils/module", "gettex
sortDisplayName
:
function
()
{
sortDisplayName
:
function
()
{
return
gettext
(
"Date added"
);
// TODO add support for sorting
return
gettext
(
"Date added"
);
// TODO add support for sorting
},
togglePreviews
:
function
(){
var
self
=
this
,
xblockUrl
=
this
.
model
.
url
();
return
$
.
ajax
({
// No runtime, so can't get this via the handler() call.
url
:
'/preview'
+
decodeURIComponent
(
xblockUrl
)
+
"/handler/trigger_previews"
,
type
:
'POST'
,
data
:
JSON
.
stringify
({
showChildrenPreviews
:
!
this
.
collection
.
showChildrenPreviews
}),
dataType
:
'json'
})
.
then
(
self
.
render
).
promise
();
}
}
});
});
...
...
cms/static/js/views/pages/container.js
View file @
b66a128c
...
@@ -140,8 +140,8 @@ define(["jquery", "underscore", "gettext", "js/views/pages/base_page", "js/views
...
@@ -140,8 +140,8 @@ define(["jquery", "underscore", "gettext", "js/views/pages/base_page", "js/views
return
this
.
xblockView
.
model
.
urlRoot
;
return
this
.
xblockView
.
model
.
urlRoot
;
},
},
onXBlockRefresh
:
function
(
xblockView
,
block_added
)
{
onXBlockRefresh
:
function
(
xblockView
,
block_added
,
is_duplicate
)
{
this
.
xblockView
.
refresh
(
block_added
);
this
.
xblockView
.
refresh
(
xblockView
,
block_added
,
is_duplicate
);
// Update publish and last modified information from the server.
// Update publish and last modified information from the server.
this
.
model
.
fetch
();
this
.
model
.
fetch
();
},
},
...
@@ -214,7 +214,7 @@ define(["jquery", "underscore", "gettext", "js/views/pages/base_page", "js/views
...
@@ -214,7 +214,7 @@ define(["jquery", "underscore", "gettext", "js/views/pages/base_page", "js/views
parent_locator
:
parentLocator
parent_locator
:
parentLocator
});
});
return
$
.
postJSON
(
this
.
getURLRoot
()
+
'/'
,
requestData
,
return
$
.
postJSON
(
this
.
getURLRoot
()
+
'/'
,
requestData
,
_
.
bind
(
this
.
onNewXBlock
,
this
,
placeholderElement
,
scrollOffset
))
_
.
bind
(
this
.
onNewXBlock
,
this
,
placeholderElement
,
scrollOffset
,
false
))
.
fail
(
function
()
{
.
fail
(
function
()
{
// Remove the placeholder if the update failed
// Remove the placeholder if the update failed
placeholderElement
.
remove
();
placeholderElement
.
remove
();
...
@@ -237,7 +237,7 @@ define(["jquery", "underscore", "gettext", "js/views/pages/base_page", "js/views
...
@@ -237,7 +237,7 @@ define(["jquery", "underscore", "gettext", "js/views/pages/base_page", "js/views
parent_locator
:
parentElement
.
data
(
'locator'
)
parent_locator
:
parentElement
.
data
(
'locator'
)
};
};
return
$
.
postJSON
(
self
.
getURLRoot
()
+
'/'
,
requestData
,
return
$
.
postJSON
(
self
.
getURLRoot
()
+
'/'
,
requestData
,
_
.
bind
(
self
.
onNewXBlock
,
self
,
placeholderElement
,
scrollOffset
))
_
.
bind
(
self
.
onNewXBlock
,
self
,
placeholderElement
,
scrollOffset
,
true
))
.
fail
(
function
()
{
.
fail
(
function
()
{
// Remove the placeholder if the update failed
// Remove the placeholder if the update failed
placeholderElement
.
remove
();
placeholderElement
.
remove
();
...
@@ -269,10 +269,10 @@ define(["jquery", "underscore", "gettext", "js/views/pages/base_page", "js/views
...
@@ -269,10 +269,10 @@ define(["jquery", "underscore", "gettext", "js/views/pages/base_page", "js/views
this
.
model
.
fetch
();
this
.
model
.
fetch
();
},
},
onNewXBlock
:
function
(
xblockElement
,
scrollOffset
,
data
)
{
onNewXBlock
:
function
(
xblockElement
,
scrollOffset
,
is_duplicate
,
data
)
{
ViewUtils
.
setScrollOffset
(
xblockElement
,
scrollOffset
);
ViewUtils
.
setScrollOffset
(
xblockElement
,
scrollOffset
);
xblockElement
.
data
(
'locator'
,
data
.
locator
);
xblockElement
.
data
(
'locator'
,
data
.
locator
);
return
this
.
refreshXBlock
(
xblockElement
,
true
);
return
this
.
refreshXBlock
(
xblockElement
,
true
,
is_duplicate
);
},
},
/**
/**
...
@@ -282,14 +282,14 @@ define(["jquery", "underscore", "gettext", "js/views/pages/base_page", "js/views
...
@@ -282,14 +282,14 @@ define(["jquery", "underscore", "gettext", "js/views/pages/base_page", "js/views
* @param element An element representing the xblock to be refreshed.
* @param element An element representing the xblock to be refreshed.
* @param block_added Flag to indicate that new block has been just added.
* @param block_added Flag to indicate that new block has been just added.
*/
*/
refreshXBlock
:
function
(
element
,
block_added
)
{
refreshXBlock
:
function
(
element
,
block_added
,
is_duplicate
)
{
var
xblockElement
=
this
.
findXBlockElement
(
element
),
var
xblockElement
=
this
.
findXBlockElement
(
element
),
parentElement
=
xblockElement
.
parent
(),
parentElement
=
xblockElement
.
parent
(),
rootLocator
=
this
.
xblockView
.
model
.
id
;
rootLocator
=
this
.
xblockView
.
model
.
id
;
if
(
xblockElement
.
length
===
0
||
xblockElement
.
data
(
'locator'
)
===
rootLocator
)
{
if
(
xblockElement
.
length
===
0
||
xblockElement
.
data
(
'locator'
)
===
rootLocator
)
{
this
.
render
({
refresh
:
true
,
block_added
:
block_added
});
this
.
render
({
refresh
:
true
,
block_added
:
block_added
});
}
else
if
(
parentElement
.
hasClass
(
'reorderable-container'
))
{
}
else
if
(
parentElement
.
hasClass
(
'reorderable-container'
))
{
this
.
refreshChildXBlock
(
xblockElement
,
block_added
);
this
.
refreshChildXBlock
(
xblockElement
,
block_added
,
is_duplicate
);
}
else
{
}
else
{
this
.
refreshXBlock
(
this
.
findXBlockElement
(
parentElement
));
this
.
refreshXBlock
(
this
.
findXBlockElement
(
parentElement
));
}
}
...
@@ -303,7 +303,7 @@ define(["jquery", "underscore", "gettext", "js/views/pages/base_page", "js/views
...
@@ -303,7 +303,7 @@ define(["jquery", "underscore", "gettext", "js/views/pages/base_page", "js/views
* refreshing.
* refreshing.
* @returns {jQuery promise} A promise representing the complete operation.
* @returns {jQuery promise} A promise representing the complete operation.
*/
*/
refreshChildXBlock
:
function
(
xblockElement
,
block_added
)
{
refreshChildXBlock
:
function
(
xblockElement
,
block_added
,
is_duplicate
)
{
var
self
=
this
,
var
self
=
this
,
xblockInfo
,
xblockInfo
,
TemporaryXBlockView
,
TemporaryXBlockView
,
...
@@ -329,7 +329,7 @@ define(["jquery", "underscore", "gettext", "js/views/pages/base_page", "js/views
...
@@ -329,7 +329,7 @@ define(["jquery", "underscore", "gettext", "js/views/pages/base_page", "js/views
});
});
return
temporaryView
.
render
({
return
temporaryView
.
render
({
success
:
function
()
{
success
:
function
()
{
self
.
onXBlockRefresh
(
temporaryView
,
block_added
);
self
.
onXBlockRefresh
(
temporaryView
,
block_added
,
is_duplicate
);
temporaryView
.
unbind
();
// Remove the temporary view
temporaryView
.
unbind
();
// Remove the temporary view
}
}
});
});
...
...
cms/static/js/views/pages/paged_container.js
View file @
b66a128c
...
@@ -6,11 +6,14 @@ define(["jquery", "underscore", "gettext", "js/views/pages/container", "js/views
...
@@ -6,11 +6,14 @@ define(["jquery", "underscore", "gettext", "js/views/pages/container", "js/views
'use strict'
;
'use strict'
;
var
PagedXBlockContainerPage
=
XBlockContainerPage
.
extend
({
var
PagedXBlockContainerPage
=
XBlockContainerPage
.
extend
({
events
:
{
"click .toggle-preview-button"
:
"toggleChildrenPreviews"
},
defaultViewClass
:
PagedContainerView
,
defaultViewClass
:
PagedContainerView
,
components_on_init
:
false
,
components_on_init
:
false
,
initialize
:
function
(
options
){
initialize
:
function
(
options
){
this
.
events
=
_
.
extend
({},
XBlockContainerPage
.
prototype
.
events
,
this
.
events
);
this
.
page_size
=
options
.
page_size
||
10
;
this
.
page_size
=
options
.
page_size
||
10
;
this
.
showChildrenPreviews
=
options
.
showChildrenPreviews
||
true
;
XBlockContainerPage
.
prototype
.
initialize
.
call
(
this
,
options
);
XBlockContainerPage
.
prototype
.
initialize
.
call
(
this
,
options
);
},
},
...
@@ -21,16 +24,28 @@ define(["jquery", "underscore", "gettext", "js/views/pages/container", "js/views
...
@@ -21,16 +24,28 @@ define(["jquery", "underscore", "gettext", "js/views/pages/container", "js/views
});
});
},
},
refreshXBlock
:
function
(
element
,
block_added
)
{
refreshXBlock
:
function
(
element
,
block_added
,
is_duplicate
)
{
var
xblockElement
=
this
.
findXBlockElement
(
element
),
var
xblockElement
=
this
.
findXBlockElement
(
element
),
rootLocator
=
this
.
xblockView
.
model
.
id
;
rootLocator
=
this
.
xblockView
.
model
.
id
;
if
(
xblockElement
.
length
===
0
||
xblockElement
.
data
(
'locator'
)
===
rootLocator
)
{
if
(
xblockElement
.
length
===
0
||
xblockElement
.
data
(
'locator'
)
===
rootLocator
)
{
this
.
render
({
refresh
:
true
,
block_added
:
block_added
});
this
.
render
({
refresh
:
true
,
block_added
:
block_added
});
}
else
{
}
else
{
this
.
refreshChildXBlock
(
xblockElement
,
block_added
);
this
.
refreshChildXBlock
(
xblockElement
,
block_added
,
is_duplicate
);
}
}
}
},
toggleChildrenPreviews
:
function
(
xblockElement
)
{
xblockElement
.
preventDefault
();
this
.
xblockView
.
togglePreviews
();
},
updatePreviewButton
:
function
(
show_previews
){
var
text
=
(
show_previews
)
?
gettext
(
'Hide Previews'
)
:
gettext
(
'Show Previews'
),
button
=
$
(
'.nav-actions .button-toggle-preview'
);
this
.
$
(
".preview-text"
,
button
).
text
(
text
);
this
.
$
(
'.toggle-preview-button'
).
removeClass
(
"is-hidden"
);
}
});
});
return
PagedXBlockContainerPage
;
return
PagedXBlockContainerPage
;
});
});
cms/static/sass/elements/_xblocks.scss
View file @
b66a128c
...
@@ -356,6 +356,10 @@
...
@@ -356,6 +356,10 @@
margin-bottom
:
0
;
margin-bottom
:
0
;
border-bottom
:
1px
solid
$gray-l4
;
border-bottom
:
1px
solid
$gray-l4
;
background-color
:
$gray-l6
;
background-color
:
$gray-l6
;
&
.is-collapsed
{
border-bottom
:
0
;
border-radius
:
3px
;
}
}
}
.xblock-render
{
.xblock-render
{
...
...
cms/templates/js/mock/mock-container-page.underscore
View file @
b66a128c
...
@@ -39,6 +39,12 @@
...
@@ -39,6 +39,12 @@
<span class="action-button-text">Add Component</span>
<span class="action-button-text">Add Component</span>
</a>
</a>
</li>
</li>
<li class="action-item action-toggle-preview nav-item">
<a href="#" class="button button-toggle-preview action-button toggle-preview-button is-hidden">
<i class="icon fa fa-refresh"></i>
<span class="action-button-text preview-text"></span>
</a>
</li>
</ul>
</ul>
</nav>
</nav>
</header>
</header>
...
...
cms/templates/js/mock/mock-paged-container-xblock.underscore
0 → 100644
View file @
b66a128c
<header class="xblock-header">
<div class="xblock-header-primary">
<div class="header-details">
<span class="xblock-display-name">Test Container</span>
</div>
<div class="header-actions">
<ul class="actions-list">
</ul>
</div>
</div>
</header>
<article class="xblock-render">
<div class="xblock" data-locator="locator-container" data-request-token="page-render-token"
data-init="MockXBlock" data-runtime-class="StudioRuntime" data-runtime-version="1">
<script type="text/template" id="paging-header-tpl">
<div class="meta-wrap">
<div class="meta">
<%= messageHtml %>
</div>
<nav class="pagination pagination-compact top">
<ol>
<li class="nav-item previous"><a class="nav-link previous-page-link" href="#"><i class="icon fa fa-angle-left"></i> <span class="nav-label">Previous</span></a></li>
<li class="nav-item next"><a class="nav-link next-page-link" href="#"><span class="nav-label">Next</span> <i class="icon fa fa-angle-right"></i></a></li>
</ol>
</nav>
</div>
</script>
<script type="text/template" id="paging-footer-tpl">
<nav class="pagination pagination-full bottom">
<ol>
<li class="nav-item previous"><a class="nav-link previous-page-link" href="#"><i class="icon fa fa-angle-left"></i> <span class="nav-label">Previous</span></a></li>
<li class="nav-item page">
<div class="pagination-form">
<label class="page-number-label" for="page-number">Page number</label>
<input id="page-number-input" class="page-number-input" name="page-number" type="text" size="4" />
</div>
<span class="current-page"><%= current_page + 1 %></span>
<span class="page-divider">/</span>
<span class="total-pages"><%= total_pages %></span>
</li>
<li class="nav-item next"><a class="nav-link next-page-link" href="#"><span class="nav-label">Next</span> <i class="icon fa fa-angle-right"></i></a></li>
</ol>
</nav>
</script>
<div class="container-paging-header"></div>
<div class="studio-xblock-wrapper" data-locator="locator-group-A">
<section class="wrapper-xblock level-nesting">
<header class="xblock-header">
<div class="xblock-header-primary">
<div class="header-details">
<a href="#" data-tooltip="Expand or Collapse" class="action expand-collapse expand">
<i class="icon fa fa-caret-down ui-toggle-expansion"></i>
<span class="sr">Expand or Collapse</span>
</a>
<span class="xblock-display-name">Group A</span>
</div>
<div class="header-actions">
<ul class="actions-list">
<li class="action-item action-drag">
<span data-tooltip="Drag to reorder" class="drag-handle action"></span>
</li>
</ul>
</div>
</div>
</header>
<article class="xblock-render">
<div class="xblock" data-request-token="page-render-token">
<div>
<div class="studio-xblock-wrapper" data-locator="locator-component-A1">
<section class="wrapper-xblock level-element">
<header class="xblock-header">
<div class="xblock-header-primary">
<div class="header-actions">
<ul class="actions-list">
<li class="action-item action-edit">
<a href="#" class="edit-button action-button"></a>
</li>
<li class="action-item action-duplicate">
<a href="#" class="duplicate-button action-button"></a>
</li>
<li class="action-item action-delete">
<a href="#" class="delete-button action-button"></a>
</li>
<li class="action-item action-drag">
<span data-tooltip="Drag to reorder" class="drag-handle action"></span>
</li>
</ul>
</div>
</div>
</header>
<article class="xblock-render"></article>
</section>
</div>
<div class="studio-xblock-wrapper" data-locator="locator-component-A2">
<section class="wrapper-xblock level-element">
<header class="xblock-header">
<div class="header-actions">
<div class="xblock-header-primary">
<ul class="actions-list">
<li class="action-item action-edit">
<a href="#" class="edit-button action-button"></a>
</li>
<li class="action-item action-duplicate">
<a href="#" class="duplicate-button action-button"></a>
</li>
<li class="action-item action-delete">
<a href="#" class="delete-button action-button"></a>
</li>
<li class="action-item action-drag">
<span data-tooltip="Drag to reorder" class="drag-handle action"></span>
</li>
</ul>
</div>
</div>
</header>
<article class="xblock-render"></article>
</section>
</div>
<div class="studio-xblock-wrapper" data-locator="locator-component-A3">
<section class="wrapper-xblock level-element">
<header class="xblock-header">
<div class="xblock-header-primary">
<div class="header-actions">
<ul class="actions-list">
<li class="action-item action-edit">
<a href="#" class="edit-button action-button"></a>
</li>
<li class="action-item action-duplicate">
<a href="#" class="duplicate-button action-button"></a>
</li>
<li class="action-item action-delete">
<a href="#" class="delete-button action-button"></a>
</li>
<li class="action-item action-drag">
<span data-tooltip="Drag to reorder" class="drag-handle action"></span>
</li>
</ul>
</div>
</div>
</header>
<article class="xblock-render"></article>
</section>
</div>
</div>
<div class="add-xblock-component new-component-item adding"></div>
</div>
</article>
</section>
</div>
<div class="studio-xblock-wrapper" data-locator="locator-group-B">
<section class="wrapper-xblock level-nesting">
<header class="xblock-header">
<div class="xblock-header-primary">
<div class="header-details">
<a href="#" data-tooltip="Expand or Collapse" class="action expand-collapse expand">
<i class="icon fa fa-caret-down ui-toggle-expansion"></i>
<span class="sr">Expand or Collapse</span>
</a>
<span class="xblock-display-name">Group B</span>
</div>
<div class="header-actions">
<ul class="actions-list">
<li class="action-item action-drag">
<span data-tooltip="Drag to reorder" class="drag-handle action"></span>
</li>
</ul>
</div>
</div>
</header>
<article class="xblock-render">
<div class="xblock" data-request-token="page-render-token">
<div>
<div class="studio-xblock-wrapper" data-locator="locator-component-B1">
<section class="wrapper-xblock level-element">
<header class="xblock-header">
<div class="xblock-header-primary">
<div class="header-actions">
<ul class="actions-list">
<li class="action-item action-edit">
<a href="#" class="edit-button action-button"></a>
</li>
<li class="action-item action-duplicate">
<a href="#" class="duplicate-button action-button"></a>
</li>
<li class="action-item action-delete">
<a href="#" class="delete-button action-button"></a>
</li>
<li class="action-item action-drag">
<span data-tooltip="Drag to reorder" class="drag-handle action"></span>
</li>
</ul>
</div>
</div>
</header>
<article class="xblock-render"></article>
</section>
</div>
<div class="studio-xblock-wrapper" data-locator="locator-component-B2">
<section class="wrapper-xblock level-element">
<header class="xblock-header">
<div class="xblock-header-primary">
<div class="header-actions">
<ul class="actions-list">
<li class="action-item action-edit">
<a href="#" class="edit-button action-button"></a>
</li>
<li class="action-item action-duplicate">
<a href="#" class="duplicate-button action-button"></a>
</li>
<li class="action-item action-delete">
<a href="#" class="delete-button action-button"></a>
</li>
<li class="action-item action-drag">
<span data-tooltip="Drag to reorder" class="drag-handle action"></span>
</li>
</ul>
</div>
</div>
</header>
<article class="xblock-render"></article>
</section>
</div>
<div class="studio-xblock-wrapper" data-locator="locator-component-B3">
<section class="wrapper-xblock level-element">
<header class="xblock-header">
<div class="xblock-header-primary">
<div class="header-actions">
<ul class="actions-list">
<li class="action-item action-edit">
<a href="#" class="edit-button action-button"></a>
</li>
<li class="action-item action-duplicate">
<a href="#" class="duplicate-button action-button"></a>
</li>
<li class="action-item action-delete">
<a href="#" class="delete-button action-button"></a>
</li>
<li class="action-item action-drag">
<span data-tooltip="Drag to reorder" class="drag-handle action"></span>
</li>
</ul>
</div>
</div>
</header>
<article class="xblock-render"></article>
</section>
</div>
</div>
<div class="add-xblock-component new-component-item adding"></div>
</div>
</article>
</section>
<div class="container-paging-footer"></div>
</div>
</article>
cms/templates/library.html
View file @
b66a128c
...
@@ -27,7 +27,8 @@ from django.utils.translation import ugettext as _
...
@@ -27,7 +27,8 @@ from django.utils.translation import ugettext as _
{
{
isUnitPage: false,
isUnitPage: false,
page_size: 10,
page_size: 10,
canEdit: ${"true" if can_edit else "false"}
canEdit: ${"true" if can_edit else "false"},
showChildrenPreviews: ${'true' if show_children_previews else 'false'}
}
}
);
);
});
});
...
@@ -53,7 +54,13 @@ from django.utils.translation import ugettext as _
...
@@ -53,7 +54,13 @@ from django.utils.translation import ugettext as _
<a
href=
"#"
class=
"button new-button new-component-button"
>
<a
href=
"#"
class=
"button new-button new-component-button"
>
<i
class=
"icon fa fa-plus icon-inline"
></i>
<span
class=
"action-button-text"
>
${_("Add Component")}
</span>
<i
class=
"icon fa fa-plus icon-inline"
></i>
<span
class=
"action-button-text"
>
${_("Add Component")}
</span>
</a>
</a>
</li>
</li>
<li
class=
"action-item action-toggle-preview nav-item"
>
<a
href=
"#"
class=
"button button-toggle-preview action-button toggle-preview-button is-hidden"
>
<i
class=
"icon fa fa-refresh"
></i>
<span
class=
"action-button-text preview-text"
></span>
</a>
</li>
</ul>
</ul>
</nav>
</nav>
</header>
</header>
...
...
cms/templates/studio_xblock_wrapper.html
View file @
b66a128c
...
@@ -47,7 +47,11 @@ messages = json.dumps(xblock.validate().to_json())
...
@@ -47,7 +47,11 @@ messages = json.dumps(xblock.validate().to_json())
% endif
% endif
<header
class=
"xblock-header xblock-header-${xblock.category}"
>
<header
class=
"xblock-header xblock-header-${xblock.category}"
>
<div
class=
"xblock-header-primary"
>
<div
class=
"xblock-header-primary
% if not show_preview:
is-collapsed
% endif
"
>
<div
class=
"header-details"
>
<div
class=
"header-details"
>
% if show_inline:
% if show_inline:
<a
href=
"#"
data-tooltip=
"${_('Expand or Collapse')}"
class=
"action expand-collapse collapse"
>
<a
href=
"#"
data-tooltip=
"${_('Expand or Collapse')}"
class=
"action expand-collapse collapse"
>
...
@@ -128,14 +132,17 @@ messages = json.dumps(xblock.validate().to_json())
...
@@ -128,14 +132,17 @@ messages = json.dumps(xblock.validate().to_json())
<div
class=
"wrapper-xblock-message xblock-validation-messages"
data-locator=
"${xblock.location | h}"
/>
<div
class=
"wrapper-xblock-message xblock-validation-messages"
data-locator=
"${xblock.location | h}"
/>
% endif
% endif
% if is_root or not xblock_url:
% if show_preview:
<article
class=
"xblock-render"
>
% if is_root or not xblock_url:
${content}
<article
class=
"xblock-render"
>
</article>
${content}
% else:
</article>
<div
class=
"xblock-message-area"
>
% else:
${content}
<div
class=
"xblock-message-area"
>
% endif
${content}
</div>
% endif
% endif
% if not is_root:
% if not is_root:
</section>
</section>
...
...
common/lib/xmodule/xmodule/library_root_xblock.py
View file @
b66a128c
...
@@ -3,11 +3,12 @@
...
@@ -3,11 +3,12 @@
"""
"""
import
logging
import
logging
from
xblock.core
import
XBlock
from
xblock.fields
import
Scope
,
String
,
List
from
xblock.fragment
import
Fragment
from
xmodule.studio_editable
import
StudioEditableModule
from
xmodule.studio_editable
import
StudioEditableModule
from
xblock.fields
import
Scope
,
String
,
List
,
Boolean
from
xblock.fragment
import
Fragment
from
xblock.core
import
XBlock
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
# Make '_' a no-op so we can scrape strings
# Make '_' a no-op so we can scrape strings
...
@@ -32,6 +33,12 @@ class LibraryRoot(XBlock):
...
@@ -32,6 +33,12 @@ class LibraryRoot(XBlock):
scope
=
Scope
.
settings
,
scope
=
Scope
.
settings
,
xml_node
=
True
,
xml_node
=
True
,
)
)
show_children_previews
=
Boolean
(
display_name
=
"Hide children preview"
,
help
=
"Choose if preview of library contents is shown"
,
scope
=
Scope
.
user_state
,
default
=
True
)
has_children
=
True
has_children
=
True
has_author_view
=
True
has_author_view
=
True
...
@@ -69,10 +76,22 @@ class LibraryRoot(XBlock):
...
@@ -69,10 +76,22 @@ class LibraryRoot(XBlock):
children_to_show
=
self
.
children
[
item_start
:
item_end
]
# pylint: disable=no-member
children_to_show
=
self
.
children
[
item_start
:
item_end
]
# pylint: disable=no-member
force_render
=
context
.
get
(
'force_render'
,
None
)
for
child_key
in
children_to_show
:
# pylint: disable=E1101
for
child_key
in
children_to_show
:
# pylint: disable=E1101
# Children must have a separate context from the library itself. Make a copy.
child_context
=
context
.
copy
()
child_context
[
'show_preview'
]
=
self
.
show_children_previews
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
)
rendered_child
=
self
.
runtime
.
render_child
(
child
,
child_view_name
,
context
)
if
unicode
(
child
.
location
)
==
force_render
:
child_context
[
'show_preview'
]
=
True
if
child_context
[
'show_preview'
]:
rendered_child
=
self
.
runtime
.
render_child
(
child
,
child_view_name
,
child_context
)
else
:
rendered_child
=
self
.
runtime
.
render_child_placeholder
(
child
,
child_view_name
,
child_context
)
fragment
.
add_frag_resources
(
rendered_child
)
fragment
.
add_frag_resources
(
rendered_child
)
contents
.
append
({
contents
.
append
({
...
@@ -87,7 +106,8 @@ class LibraryRoot(XBlock):
...
@@ -87,7 +106,8 @@ class LibraryRoot(XBlock):
'can_add'
:
can_add
,
'can_add'
:
can_add
,
'first_displayed'
:
item_start
,
'first_displayed'
:
item_start
,
'total_children'
:
children_count
,
'total_children'
:
children_count
,
'displayed_children'
:
len
(
children_to_show
)
'displayed_children'
:
len
(
children_to_show
),
'previews'
:
self
.
show_children_previews
})
})
)
)
...
@@ -106,3 +126,9 @@ class LibraryRoot(XBlock):
...
@@ -106,3 +126,9 @@ class LibraryRoot(XBlock):
Always returns the raw 'library' field from the key.
Always returns the raw 'library' field from the key.
"""
"""
return
self
.
scope_ids
.
usage_id
.
course_key
.
library
return
self
.
scope_ids
.
usage_id
.
course_key
.
library
@XBlock.json_handler
def
trigger_previews
(
self
,
request_body
,
suffix
):
# pylint: disable=unused-argument
""" Enable or disable previews in studio for library children. """
self
.
show_children_previews
=
request_body
.
get
(
'showChildrenPreviews'
,
self
.
show_children_previews
)
return
{
'showChildrenPreviews'
:
self
.
show_children_previews
}
common/test/acceptance/pages/studio/container.py
View file @
b66a128c
...
@@ -500,6 +500,12 @@ class XBlockWrapper(PageObject):
...
@@ -500,6 +500,12 @@ class XBlockWrapper(PageObject):
"""
"""
self
.
q
(
css
=
self
.
_bounded_selector
(
'span.message-text a'
))
.
first
.
click
()
self
.
q
(
css
=
self
.
_bounded_selector
(
'span.message-text a'
))
.
first
.
click
()
def
is_placeholder
(
self
):
"""
Checks to see if the XBlock is rendered as a placeholder without a preview.
"""
return
not
self
.
q
(
css
=
self
.
_bounded_selector
(
'.wrapper-xblock article'
))
.
present
@property
@property
def
group_configuration_link_name
(
self
):
def
group_configuration_link_name
(
self
):
"""
"""
...
...
common/test/acceptance/pages/studio/library.py
View file @
b66a128c
...
@@ -69,6 +69,25 @@ class LibraryEditPage(LibraryPage, PaginatedMixin):
...
@@ -69,6 +69,25 @@ class LibraryEditPage(LibraryPage, PaginatedMixin):
"""
"""
return
self
.
_get_xblocks
()
return
self
.
_get_xblocks
()
def
are_previews_showing
(
self
):
"""
Determines whether or not previews are showing for XBlocks
"""
return
all
([
not
xblock
.
is_placeholder
()
for
xblock
in
self
.
xblocks
])
def
toggle_previews
(
self
):
"""
Clicks the preview toggling button and waits for the previews to appear or disappear.
"""
toggle
=
not
self
.
are_previews_showing
()
self
.
q
(
css
=
'.toggle-preview-button'
)
.
click
()
EmptyPromise
(
lambda
:
self
.
are_previews_showing
()
==
toggle
,
'Preview is visible:
%
s'
%
toggle
,
timeout
=
30
)
.
fulfill
()
self
.
wait_until_ready
()
def
click_duplicate_button
(
self
,
xblock_id
):
def
click_duplicate_button
(
self
,
xblock_id
):
"""
"""
Click on the duplicate button for the given XBlock
Click on the duplicate button for the given XBlock
...
...
common/test/acceptance/tests/studio/test_studio_library.py
View file @
b66a128c
...
@@ -51,7 +51,6 @@ class LibraryEditPageTest(StudioLibraryTest):
...
@@ -51,7 +51,6 @@ class LibraryEditPageTest(StudioLibraryTest):
Then one XBlock is displayed
Then one XBlock is displayed
And displayed XBlock are second one
And displayed XBlock are second one
"""
"""
self
.
browser
.
save_screenshot
(
'library_page'
)
self
.
assertEqual
(
len
(
self
.
lib_page
.
xblocks
),
0
)
self
.
assertEqual
(
len
(
self
.
lib_page
.
xblocks
),
0
)
# Create a new block:
# Create a new block:
...
@@ -343,6 +342,161 @@ class LibraryNavigationTest(StudioLibraryTest):
...
@@ -343,6 +342,161 @@ class LibraryNavigationTest(StudioLibraryTest):
self
.
assertEqual
(
self
.
lib_page
.
xblocks
[
-
1
]
.
name
,
'11'
)
self
.
assertEqual
(
self
.
lib_page
.
xblocks
[
-
1
]
.
name
,
'11'
)
self
.
assertEqual
(
self
.
lib_page
.
get_page_number
(),
'1'
)
self
.
assertEqual
(
self
.
lib_page
.
get_page_number
(),
'1'
)
def
test_previews
(
self
):
"""
Scenario: Ensure the user is able to hide previews of XBlocks.
Given that I have a library in Studio with 40 XBlocks
Then previews are visible
And when I click the toggle previews button
Then the previews will not be visible
And when I click the toggle previews button
Then the previews are visible
"""
self
.
assertTrue
(
self
.
lib_page
.
are_previews_showing
())
self
.
lib_page
.
toggle_previews
()
self
.
assertFalse
(
self
.
lib_page
.
are_previews_showing
())
self
.
lib_page
.
toggle_previews
()
self
.
assertTrue
(
self
.
lib_page
.
are_previews_showing
())
def
test_previews_navigation
(
self
):
"""
Scenario: Ensure preview settings persist across navigation.
Given that I have a library in Studio with 40 XBlocks
Then previews are visible
And when I click the toggle previews button
And click the next page button
Then the previews will not be visible
And the first XBlock will be the 11th one
And the last XBlock will be the 20th one
And when I click the toggle previews button
And I click the previous page button
Then the previews will be visible
And the first XBlock will be the first one
And the last XBlock will be the 11th one
"""
self
.
assertTrue
(
self
.
lib_page
.
are_previews_showing
())
self
.
lib_page
.
toggle_previews
()
# Which set of arrows shouldn't matter for this test.
self
.
lib_page
.
move_forward
(
'top'
)
self
.
assertFalse
(
self
.
lib_page
.
are_previews_showing
())
self
.
assertEqual
(
self
.
lib_page
.
xblocks
[
0
]
.
name
,
'11'
)
self
.
assertEqual
(
self
.
lib_page
.
xblocks
[
-
1
]
.
name
,
'20'
)
self
.
lib_page
.
toggle_previews
()
self
.
lib_page
.
move_back
(
'top'
)
self
.
assertTrue
(
self
.
lib_page
.
are_previews_showing
())
self
.
assertEqual
(
self
.
lib_page
.
xblocks
[
0
]
.
name
,
'1'
)
self
.
assertEqual
(
self
.
lib_page
.
xblocks
[
-
1
]
.
name
,
'10'
)
def
test_preview_state_persistance
(
self
):
"""
Scenario: Ensure preview state persists between page loads.
Given that I have a library in Studio with 40 XBlocks
Then previews are visible
And when I click the toggle previews button
And I revisit the page
Then the previews will not be visible
"""
self
.
assertTrue
(
self
.
lib_page
.
are_previews_showing
())
self
.
lib_page
.
toggle_previews
()
self
.
lib_page
.
visit
()
self
.
lib_page
.
wait_until_ready
()
self
.
assertFalse
(
self
.
lib_page
.
are_previews_showing
())
def
test_preview_add_xblock
(
self
):
"""
Scenario: Ensure previews are shown when adding new blocks, regardless of preview setting.
Given that I have a library in Studio with 40 XBlocks
Then previews are visible
And when I click the toggle previews button
Then the previews will not be visible
And when I add an XBlock
Then I will be on the 5th page
And the XBlock will have loaded a preview
And when I revisit the library
And I go to the 5th page
Then the top XBlock will be the one I added
And it will not have a preview
And when I add an XBlock
Then the XBlock I added will have a preview
And the top XBlock will not have one.
"""
self
.
assertTrue
(
self
.
lib_page
.
are_previews_showing
())
self
.
lib_page
.
toggle_previews
()
self
.
assertFalse
(
self
.
lib_page
.
are_previews_showing
())
add_component
(
self
.
lib_page
,
"problem"
,
"Checkboxes"
)
self
.
assertEqual
(
self
.
lib_page
.
get_page_number
(),
'5'
)
first_added
=
self
.
lib_page
.
xblocks
[
0
]
self
.
assertIn
(
"Checkboxes"
,
first_added
.
name
)
self
.
assertFalse
(
self
.
lib_page
.
xblocks
[
0
]
.
is_placeholder
())
self
.
lib_page
.
visit
()
self
.
lib_page
.
wait_until_ready
()
self
.
lib_page
.
go_to_page
(
5
)
self
.
assertTrue
(
self
.
lib_page
.
xblocks
[
0
]
.
is_placeholder
())
add_component
(
self
.
lib_page
,
"problem"
,
"Multiple Choice"
)
# DOM has detatched the element since last assignment
first_added
=
self
.
lib_page
.
xblocks
[
0
]
second_added
=
self
.
lib_page
.
xblocks
[
1
]
self
.
assertIn
(
"Multiple Choice"
,
second_added
.
name
)
self
.
assertFalse
(
second_added
.
is_placeholder
())
self
.
assertTrue
(
first_added
.
is_placeholder
())
def
test_edit_with_preview
(
self
):
"""
Scenario: Editing an XBlock should show me a preview even if previews are hidden.
Given that I have a library in Studio with 40 XBlocks
Then previews are visible
And when I click the toggle previews button
Then the previews will not be visible
And when I edit the first XBlock
Then the first XBlock will show a preview
And the other XBlocks will still be placeholders
"""
self
.
assertTrue
(
self
.
lib_page
.
are_previews_showing
())
self
.
lib_page
.
toggle_previews
()
self
.
assertFalse
(
self
.
lib_page
.
are_previews_showing
())
target
=
self
.
lib_page
.
xblocks
[
0
]
target
.
edit
()
target
.
save_settings
()
self
.
assertFalse
(
target
.
is_placeholder
())
self
.
assertTrue
(
all
([
xblock
.
is_placeholder
()
for
xblock
in
self
.
lib_page
.
xblocks
[
1
:]]))
def
test_duplicate_xblock_pagination
(
self
):
"""
Scenario: Duplicating an XBlock should not shift the page if the XBlock is not at the end.
Given that I have a library in Studio with 40 XBlocks
When I duplicate the third XBlock
Then the page should not change
And the duplicate XBlock should be there
And it should show a preview
And there should not be more than 10 XBlocks visible.
"""
third_block_id
=
self
.
lib_page
.
xblocks
[
2
]
.
locator
self
.
lib_page
.
click_duplicate_button
(
third_block_id
)
self
.
lib_page
.
wait_until_ready
()
target
=
self
.
lib_page
.
xblocks
[
3
]
self
.
assertIn
(
'Duplicate'
,
target
.
name
)
self
.
assertFalse
(
target
.
is_placeholder
())
self
.
assertEqual
(
len
(
self
.
lib_page
.
xblocks
),
10
)
def
test_duplicate_xblock_pagination_end
(
self
):
"""
Scenario: Duplicating an XBlock if it's the last one should bring me to the next page with a preview.
Given that I have a library in Studio with 40 XBlocks
And when I hide previews
And I duplicate the last XBlock
The page should change to page 2
And the duplicate XBlock should be the first XBlock
And it should not be a placeholder
"""
self
.
lib_page
.
toggle_previews
()
last_block_id
=
self
.
lib_page
.
xblocks
[
-
1
]
.
locator
self
.
lib_page
.
click_duplicate_button
(
last_block_id
)
self
.
lib_page
.
wait_until_ready
()
self
.
assertEqual
(
self
.
lib_page
.
get_page_number
(),
'2'
)
target_block
=
self
.
lib_page
.
xblocks
[
0
]
self
.
assertIn
(
'Duplicate'
,
target_block
.
name
)
self
.
assertFalse
(
target_block
.
is_placeholder
())
class
LibraryUsersPageTest
(
StudioLibraryTest
):
class
LibraryUsersPageTest
(
StudioLibraryTest
):
"""
"""
...
...
lms/templates/studio_render_paged_children_view.html
View file @
b66a128c
...
@@ -8,7 +8,12 @@
...
@@ -8,7 +8,12 @@
</script>
</script>
% endfor
% endfor
<div
class=
"xblock-container-paging-parameters"
data-start=
"${first_displayed}"
data-displayed=
"${displayed_children}"
data-total=
"${total_children}"
></div>
<div
class=
"xblock-container-paging-parameters"
data-start=
"${first_displayed}"
data-displayed=
"${displayed_children}"
data-total=
"${total_children}"
data-previews=
"${'true' if previews else 'false'}"
></div>
<div
class=
"container-paging-header"
></div>
<div
class=
"container-paging-header"
></div>
...
...
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