diff --git a/cms/djangoapps/contentstore/views/component.py b/cms/djangoapps/contentstore/views/component.py index c1338d6230..68650e9bc1 100644 --- a/cms/djangoapps/contentstore/views/component.py +++ b/cms/djangoapps/contentstore/views/component.py @@ -23,7 +23,6 @@ from xblock.exceptions import NoSuchHandlerError from xblock.fields import Scope from xblock.plugin import PluginMissingError from xblock.runtime import Mixologist -from xmodule.x_module import prefer_xmodules from lms.lib.xblock.runtime import unquote_slashes @@ -310,13 +309,20 @@ def container_handler(request, tag=None, package_id=None, branch=None, version_g old_location, course, xblock, __ = _get_item_in_course(request, locator) except ItemNotFoundError: return HttpResponseBadRequest() - parent_xblock = get_parent_xblock(xblock) + + ancestor_xblocks = [] + parent = get_parent_xblock(xblock) + while parent and parent.category != 'sequential': + ancestor_xblocks.append(parent) + parent = get_parent_xblock(parent) + + ancestor_xblocks.reverse() return render_to_response('container.html', { 'context_course': course, 'xblock': xblock, 'xblock_locator': locator, - 'parent_xblock': parent_xblock, + 'ancestor_xblocks': ancestor_xblocks, }) else: return HttpResponseBadRequest("Only supports html requests") diff --git a/cms/djangoapps/contentstore/views/item.py b/cms/djangoapps/contentstore/views/item.py index d9734773b0..667bf91a0d 100644 --- a/cms/djangoapps/contentstore/views/item.py +++ b/cms/djangoapps/contentstore/views/item.py @@ -206,12 +206,10 @@ def xblock_view_handler(request, package_id, view_name, tag=None, branch=None, v elif view_name == 'student_view' and component.has_children: # For non-leaf xblocks on the unit page, show the special rendering # which links to the new container page. - course_location = loc_mapper().translate_locator_to_location(locator, True) - course = store.get_item(course_location) - html = render_to_string('unit_container_xblock_component.html', { - 'course': course, + html = render_to_string('container_xblock_component.html', { 'xblock': component, - 'locator': locator + 'locator': locator, + 'reordering_enabled': True, }) return JsonResponse({ 'html': html, diff --git a/cms/djangoapps/contentstore/views/preview.py b/cms/djangoapps/contentstore/views/preview.py index 8307c88d64..b3e4f0562e 100644 --- a/cms/djangoapps/contentstore/views/preview.py +++ b/cms/djangoapps/contentstore/views/preview.py @@ -179,6 +179,8 @@ def _studio_wrap_xblock(xblock, view, frag, context, display_name_only=False): } if xblock.category == 'vertical': template = 'studio_vertical_wrapper.html' + elif xblock.location != context.get('root_xblock').location and xblock.has_children: + template = 'container_xblock_component.html' else: template = 'studio_xblock_wrapper.html' html = render_to_string(template, template_context) diff --git a/cms/djangoapps/contentstore/views/tests/test_container.py b/cms/djangoapps/contentstore/views/tests/test_container.py index b1a6faf96f..766b86f8f3 100644 --- a/cms/djangoapps/contentstore/views/tests/test_container.py +++ b/cms/djangoapps/contentstore/views/tests/test_container.py @@ -26,8 +26,44 @@ class ContainerViewTestCase(CourseTestCase): category="video", display_name="My Video") def test_container_html(self): - url = xblock_studio_url(self.child_vertical) + self._test_html_content( + self.child_vertical, + expected_section_tag='
', + expected_breadcrumbs=( + r'Unit\s*' + r'Child Vertical'), + ) + + def test_container_on_container_html(self): + """ + Create the scenario of an xblock with children (non-vertical) on the container page. + This should create a container page that is a child of another container page. + """ + xblock_with_child = ItemFactory.create(parent_location=self.child_vertical.location, + category="wrapper", display_name="Wrapper") + ItemFactory.create(parent_location=xblock_with_child.location, + category="html", display_name="Child HTML") + self._test_html_content( + xblock_with_child, + expected_section_tag='
', + expected_breadcrumbs=( + r'Unit\s*' + r'Child Vertical\s*' + r'Wrapper'), + ) + + def _test_html_content(self, xblock, expected_section_tag, expected_breadcrumbs): + """ + Get the HTML for a container page and verify the section tag is correct + and the breadcrumbs trail is correct. + """ + url = xblock_studio_url(xblock, self.course) resp = self.client.get_html(url) self.assertEqual(resp.status_code, 200) html = resp.content - self.assertIn('
', html) + self.assertIn(expected_section_tag, html) + # Verify the navigation link at the top of the page is correct. + self.assertRegexpMatches(html, expected_breadcrumbs) diff --git a/cms/djangoapps/contentstore/views/tests/test_item.py b/cms/djangoapps/contentstore/views/tests/test_item.py index 3aa3f15ad9..2b225b9d8e 100644 --- a/cms/djangoapps/contentstore/views/tests/test_item.py +++ b/cms/djangoapps/contentstore/views/tests/test_item.py @@ -132,6 +132,31 @@ class GetItem(ItemTest): # Verify that the Studio element wrapper has been added self.assertIn('level-element', html) + def test_get_container_nested_container_fragment(self): + """ + Test the case of the container page containing a link to another container page. + """ + # Add a wrapper with child beneath a child vertical + root_locator = self._create_vertical() + + resp = self.create_xblock(parent_locator=root_locator, category="wrapper") + self.assertEqual(resp.status_code, 200) + wrapper_locator = self.response_locator(resp) + + resp = self.create_xblock(parent_locator=wrapper_locator, category='problem', boilerplate='multiplechoice.yaml') + self.assertEqual(resp.status_code, 200) + + # Get the preview HTML and verify the View -> link is present. + html, __ = self._get_container_preview(root_locator) + self.assertIn('wrapper-xblock', html) + self.assertRegexpMatches( + html, + # The instance of the wrapper class will have an auto-generated ID (wrapperxxx). Allow anything + # for the 3 characters after wrapper. + (r'"/container/MITx.999.Robot_Super_Course/branch/published/block/wrapper.{3}" class="action-button">\s*' + 'View') + ) + class DeleteItem(ItemTest): """Tests for '/xblock' DELETE url.""" diff --git a/cms/static/sass/_base.scss b/cms/static/sass/_base.scss index f921e2c6a7..4fbe6e6091 100644 --- a/cms/static/sass/_base.scss +++ b/cms/static/sass/_base.scss @@ -334,11 +334,12 @@ p, ul, ol, dl { .navigation-link { @extend %cont-truncated; display: inline-block; - max-width: 150px; + max-width: 250px; &.navigation-current { @extend %ui-disabled; color: $gray; + max-width: 250px; &:before { color: $gray; diff --git a/cms/static/sass/elements/_xblocks.scss b/cms/static/sass/elements/_xblocks.scss index 1fccbf9cb8..6ab90b0e66 100644 --- a/cms/static/sass/elements/_xblocks.scss +++ b/cms/static/sass/elements/_xblocks.scss @@ -55,7 +55,7 @@ } // UI: xblock is collapsible -.wrapper-xblock.is-collapsible { +.wrapper-xblock.is-collapsible, .wrapper-xblock.xblock-type-container { [class^="icon-"] { font-style: normal; diff --git a/cms/templates/container.html b/cms/templates/container.html index 39ef7cfd13..83855250ed 100644 --- a/cms/templates/container.html +++ b/cms/templates/container.html @@ -16,7 +16,7 @@ from django.utils.translation import ugettext as _ <% xblock_info = { 'id': str(xblock_locator), - 'display-name': xblock.display_name, + 'display-name': xblock.display_name_with_default, 'category': xblock.category, }; %> @@ -47,14 +47,16 @@ xblock_info = {

- <% - parent_url = xblock_studio_url(parent_xblock, context_course) - %> - % if parent_url: - ${parent_xblock.display_name | h} - % endif - ${xblock.display_name | h} + % for ancestor in ancestor_xblocks: + <% + ancestor_url = xblock_studio_url(ancestor, context_course) + %> + % if ancestor_url: + ${ancestor.display_name_with_default | h} + % endif + % endfor + ${xblock.display_name_with_default | h}

diff --git a/cms/templates/unit_container_xblock_component.html b/cms/templates/container_xblock_component.html similarity index 72% rename from cms/templates/unit_container_xblock_component.html rename to cms/templates/container_xblock_component.html index 3440626dd9..cd91f8b50f 100644 --- a/cms/templates/unit_container_xblock_component.html +++ b/cms/templates/container_xblock_component.html @@ -7,12 +7,12 @@ from contentstore.views.helpers import xblock_studio_url
- ${xblock.display_name} + ${xblock.display_name_with_default}
- + ## We currently support reordering only on the unit page. + % if reordering_enabled: + + % endif
diff --git a/cms/templates/studio_vertical_wrapper.html b/cms/templates/studio_vertical_wrapper.html index 774b49bf95..226811d790 100644 --- a/cms/templates/studio_vertical_wrapper.html +++ b/cms/templates/studio_vertical_wrapper.html @@ -8,7 +8,7 @@ ${_('Expand or Collapse')} - ${xblock.display_name | h} + ${xblock.display_name_with_default | h}
    diff --git a/cms/templates/studio_xblock_wrapper.html b/cms/templates/studio_xblock_wrapper.html index 870c2509b4..cd97eeb7fd 100644 --- a/cms/templates/studio_xblock_wrapper.html +++ b/cms/templates/studio_xblock_wrapper.html @@ -8,7 +8,7 @@ % endif
    - ${xblock.display_name | h} + ${xblock.display_name_with_default | h}