diff --git a/cms/djangoapps/contentstore/views/item.py b/cms/djangoapps/contentstore/views/item.py index 67f1dc268e..e46d83d70b 100644 --- a/cms/djangoapps/contentstore/views/item.py +++ b/cms/djangoapps/contentstore/views/item.py @@ -237,12 +237,28 @@ def xblock_view_handler(request, usage_key_string, view_name): if view_name == 'reorderable_container_child_preview': reorderable_items.add(xblock.location) + paging = None + try: + if request.REQUEST.get('enable_paging', 'false') == 'true': + paging = { + 'page_number': int(request.REQUEST.get('page_number', 0)), + 'page_size': int(request.REQUEST.get('page_size', 0)), + } + except ValueError: + log.exception( + "Couldn't parse paging parameters: enable_paging: %s, page_number: %s, page_size: %s", + request.REQUEST.get('enable_paging', 'false'), + request.REQUEST.get('page_number', 0), + request.REQUEST.get('page_size', 0) + ) + # Set up the context to be passed to each XBlock's render method. context = { 'is_pages_view': is_pages_view, # This setting disables the recursive wrapping of xblocks 'is_unit_page': is_unit(xblock), 'root_xblock': xblock if (view_name == 'container_preview') else None, - 'reorderable_items': reorderable_items + 'reorderable_items': reorderable_items, + 'paging': paging } fragment = get_preview_fragment(request, xblock, context) diff --git a/cms/static/coffee/spec/main.coffee b/cms/static/coffee/spec/main.coffee index 3a7b2c046a..1bd1177264 100644 --- a/cms/static/coffee/spec/main.coffee +++ b/cms/static/coffee/spec/main.coffee @@ -239,6 +239,7 @@ define([ "js/spec/views/assets_spec", "js/spec/views/baseview_spec", "js/spec/views/container_spec", + "js/spec/views/library_container_spec", "js/spec/views/group_configuration_spec", "js/spec/views/paging_spec", "js/spec/views/unit_outline_spec", diff --git a/cms/static/js/factories/container.js b/cms/static/js/factories/container.js index 93cdeb8fd9..ea48bb2a98 100644 --- a/cms/static/js/factories/container.js +++ b/cms/static/js/factories/container.js @@ -1,22 +1,20 @@ define([ - 'jquery', 'js/models/xblock_info', 'js/views/pages/container', + 'jquery', 'underscore', 'js/models/xblock_info', 'js/views/pages/container', 'js/collections/component_template', 'xmodule', 'coffee/src/main', 'xblock/cms.runtime.v1' ], -function($, XBlockInfo, ContainerPage, ComponentTemplates, xmoduleLoader) { +function($, _, XBlockInfo, ContainerPage, ComponentTemplates, xmoduleLoader) { 'use strict'; - return function (componentTemplates, XBlockInfoJson, action, isUnitPage) { - var templates = new ComponentTemplates(componentTemplates, {parse: true}), - mainXBlockInfo = new XBlockInfo(XBlockInfoJson, {parse: true}); + return function (componentTemplates, XBlockInfoJson, action, options) { + var main_options = { + el: $('#content'), + model: new XBlockInfo(XBlockInfoJson, {parse: true}), + action: action, + templates: new ComponentTemplates(componentTemplates, {parse: true}) + }; xmoduleLoader.done(function () { - var view = new ContainerPage({ - el: $('#content'), - model: mainXBlockInfo, - action: action, - templates: templates, - isUnitPage: isUnitPage - }); + var view = new ContainerPage(_.extend(main_options, options)); view.render(); }); }; diff --git a/cms/static/js/factories/library.js b/cms/static/js/factories/library.js index 2729a3cf27..e7834f60ef 100644 --- a/cms/static/js/factories/library.js +++ b/cms/static/js/factories/library.js @@ -1,22 +1,20 @@ define([ - 'jquery', 'js/models/xblock_info', 'js/views/pages/container', + 'jquery', 'underscore', 'js/models/xblock_info', 'js/views/pages/container', 'js/collections/component_template', 'xmodule', 'coffee/src/main', 'xblock/cms.runtime.v1' ], -function($, XBlockInfo, ContainerPage, ComponentTemplates, xmoduleLoader) { +function($, _, XBlockInfo, ContainerPage, ComponentTemplates, xmoduleLoader) { 'use strict'; - return function (componentTemplates, XBlockInfoJson) { - var templates = new ComponentTemplates(componentTemplates, {parse: true}), - mainXBlockInfo = new XBlockInfo(XBlockInfoJson, {parse: true}); + return function (componentTemplates, XBlockInfoJson, options) { + var main_options = { + el: $('#content'), + model: new XBlockInfo(XBlockInfoJson, {parse: true}), + templates: new ComponentTemplates(componentTemplates, {parse: true}), + action: 'view' + }; xmoduleLoader.done(function () { - var view = new ContainerPage({ - el: $('#content'), - model: mainXBlockInfo, - action: "view", - templates: templates, - isUnitPage: false - }); + var view = new ContainerPage(_.extend(main_options, options)); view.render(); }); }; diff --git a/cms/static/js/spec/views/library_container_spec.js b/cms/static/js/spec/views/library_container_spec.js new file mode 100644 index 0000000000..2d39cdc358 --- /dev/null +++ b/cms/static/js/spec/views/library_container_spec.js @@ -0,0 +1,489 @@ +define([ "jquery", "underscore", "js/common_helpers/ajax_helpers", "URI", "js/models/xblock_info", + "js/views/library_container", "js/views/paging_header", "js/views/paging_footer"], + function ($, _, AjaxHelpers, URI, XBlockInfo, PagedContainer, PagingContainer, PagingFooter) { + + var htmlResponseTpl = _.template('' + + '
' + ); + + function getResponseHtml(options){ + return '
' + + '
' + + htmlResponseTpl(options) + + '' + + '
' + } + + var PAGE_SIZE = 3; + + var mockFirstPage = { + resources: [], + html: getResponseHtml({ + start: 0, + displayed: PAGE_SIZE, + total: PAGE_SIZE + 1 + }) + }; + + var mockSecondPage = { + resources: [], + html: getResponseHtml({ + start: PAGE_SIZE, + displayed: 1, + total: PAGE_SIZE + 1 + }) + }; + + var mockEmptyPage = { + resources: [], + html: getResponseHtml({ + start: 0, + displayed: 0, + total: 0 + }) + }; + + var respondWithMockPage = function(requests) { + var requestIndex = requests.length - 1; + var request = requests[requestIndex]; + var url = new URI(request.url); + var queryParameters = url.query(true); // Returns an object with each query parameter stored as a value + var page = queryParameters.page_number; + var response = page === "0" ? mockFirstPage : mockSecondPage; + AjaxHelpers.respondWithJson(requests, response, requestIndex); + }; + + var MockPagingView = PagedContainer.extend({ + view: 'container_preview', + el: $("
"), + model: new XBlockInfo({}, {parse: true}) + }); + + describe("Paging Container", function() { + var pagingContainer; + + beforeEach(function () { + var feedbackTpl = readFixtures('system-feedback.underscore'); + setFixtures($(" + + +
+ +
+
+
+
+ +
+
    +
  • + +
  • +
+
+
+
+
+
+
+
+
+
+
+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+
+
+
+
+
+
+
+
+
+
+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+
+
+
+
+
+
+
+
+
+
+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+
+
+
+
+
+
+
+
+
+
+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
    +
  • + +
  • +
+
+
+
+ +
+
+
+
+
+
+
+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+
+
+
+
+
+
+
+
+
+
+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+
+
+
+
+
+
+
+
+
+
+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+
+
+
+
+
+
+
+
+
+ +
+ diff --git a/cms/templates/js/mock/mock-container-paged-xblock.underscore b/cms/templates/js/mock/mock-container-paged-xblock.underscore new file mode 100644 index 0000000000..2314bb8925 --- /dev/null +++ b/cms/templates/js/mock/mock-container-paged-xblock.underscore @@ -0,0 +1,257 @@ +
+
+
+ Test Container +
+
+
    +
+
+
+
+
+
+ + + + +
+ +
+
+
+
+ +
+
    +
  • + +
  • +
+
+
+
+
+
+
+
+
+
+
+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+
+
+
+
+
+
+
+
+
+
+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+
+
+
+
+
+
+
+
+
+
+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
    +
  • + +
  • +
+
+
+
+ +
+
+
+
+
+
+
+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+
+
+
+
+
+
+
+
+
+
+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+
+
+
+
+
+
+
+
+
+
+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+
+
+
+
+
+
+
+
+
+ +
+
diff --git a/cms/templates/library.html b/cms/templates/library.html index 70bd836bad..dc9baa5736 100644 --- a/cms/templates/library.html +++ b/cms/templates/library.html @@ -22,8 +22,12 @@ from django.utils.translation import ugettext as _ <%block name="requirejs"> require(["js/factories/library"], function(LibraryFactory) { LibraryFactory( - ${component_templates | n}, - ${json.dumps(xblock_info) | n} + ${component_templates | n}, ${json.dumps(xblock_info) | n}, + { + isUnitPage: false, + enable_paging: true, + page_size: 10 + } ); }); diff --git a/common/lib/xmodule/xmodule/library_root_xblock.py b/common/lib/xmodule/xmodule/library_root_xblock.py index dc00aaa97f..497a145b79 100644 --- a/common/lib/xmodule/xmodule/library_root_xblock.py +++ b/common/lib/xmodule/xmodule/library_root_xblock.py @@ -3,10 +3,10 @@ """ import logging -from .studio_editable import StudioEditableModule from xblock.core import XBlock from xblock.fields import Scope, String, List from xblock.fragment import Fragment +from xmodule.studio_editable import StudioEditableModule log = logging.getLogger(__name__) @@ -42,29 +42,55 @@ class LibraryRoot(XBlock): def author_view(self, context): """ - Renders the Studio preview view, which supports drag and drop. + Renders the Studio preview view. """ fragment = Fragment() + self.render_children(context, fragment, can_reorder=False, can_add=True) + return fragment + + def render_children(self, context, fragment, can_reorder=False, can_add=False): # pylint: disable=unused-argument + """ + Renders the children of the module with HTML appropriate for Studio. If can_reorder is True, + then the children will be rendered to support drag and drop. + """ contents = [] - for child_key in self.children: # pylint: disable=E1101 - context['reorderable_items'].add(child_key) + paging = context.get('paging', None) + + children_count = len(self.children) # pylint: disable=no-member + item_start, item_end = 0, children_count + + # TODO sort children + if paging: + page_number = paging.get('page_number', 0) + raw_page_size = paging.get('page_size', None) + page_size = raw_page_size if raw_page_size is not None else children_count + item_start, item_end = page_size * page_number, page_size * (page_number + 1) + + children_to_show = self.children[item_start:item_end] # pylint: disable=no-member + + for child_key in children_to_show: # pylint: disable=E1101 child = self.runtime.get_block(child_key) - rendered_child = self.runtime.render_child(child, StudioEditableModule.get_preview_view_name(child), context) + child_view_name = StudioEditableModule.get_preview_view_name(child) + rendered_child = self.runtime.render_child(child, child_view_name, context) fragment.add_frag_resources(rendered_child) contents.append({ - 'id': unicode(child_key), - 'content': rendered_child.content, + 'id': child.location.to_deprecated_string(), + 'content': rendered_child.content }) - fragment.add_content(self.runtime.render_template("studio_render_children_view.html", { - 'items': contents, - 'xblock_context': context, - 'can_add': True, - 'can_reorder': True, - })) - return fragment + fragment.add_content( + self.runtime.render_template("studio_render_paged_children_view.html", { + 'items': contents, + 'xblock_context': context, + 'can_add': can_add, + 'can_reorder': False, + 'first_displayed': item_start, + 'total_children': children_count, + 'displayed_children': len(children_to_show) + }) + ) @property def display_org_with_default(self): diff --git a/common/lib/xmodule/xmodule/video_module/video_handlers.py b/common/lib/xmodule/xmodule/video_module/video_handlers.py index 9e9db860ca..1ba427c357 100644 --- a/common/lib/xmodule/xmodule/video_module/video_handlers.py +++ b/common/lib/xmodule/xmodule/video_module/video_handlers.py @@ -155,7 +155,6 @@ class VideoStudentViewHandlers(object): if transcript_name: # Get the asset path for course - asset_path = None course = self.descriptor.runtime.modulestore.get_course(self.course_id) if course.static_asset_path: asset_path = course.static_asset_path diff --git a/lms/templates/studio_render_paged_children_view.html b/lms/templates/studio_render_paged_children_view.html new file mode 100644 index 0000000000..fe5b5403e1 --- /dev/null +++ b/lms/templates/studio_render_paged_children_view.html @@ -0,0 +1,23 @@ +<%! from django.utils.translation import ugettext as _ %> + +<%namespace name='static' file='static_content.html'/> + +% for template_name in ["paging-header", "paging-footer"]: + +% endfor + +
+ +
+ +% for item in items: + ${item['content']} +% endfor + +% if can_add: +
+% endif + +