Exemplo n.º 1
0
    def test_preview_html(self):
        """
        Checks that html for the StructuredTagsAside is generated correctly
        """
        request = RequestFactory().get('/dummy-url')
        request.user = UserFactory()
        request.session = {}

        # Call get_preview_fragment directly.
        context = {'reorderable_items': set(), 'read_only': True}
        problem_html = get_preview_fragment(request, self.problem,
                                            context).content

        parser = etree.HTMLParser()
        tree = etree.parse(StringIO(problem_html), parser)

        main_div_nodes = tree.xpath('/html/body/div/section/div')
        self.assertEquals(len(main_div_nodes), 1)

        div_node = main_div_nodes[0]
        self.assertEquals(div_node.get('data-init'), 'StructuredTagsInit')
        self.assertEquals(div_node.get('data-runtime-class'), 'PreviewRuntime')
        self.assertEquals(div_node.get('data-block-type'), 'tagging_aside')
        self.assertEquals(div_node.get('data-runtime-version'), '1')
        self.assertIn('xblock_asides-v1', div_node.get('class'))

        select_nodes = div_node.xpath('div/select')
        self.assertEquals(len(select_nodes), 2)

        select_node1 = select_nodes[0]
        self.assertEquals(select_node1.get('name'), self.aside_tag_dif)

        option_nodes1 = select_node1.xpath('option')
        self.assertEquals(len(option_nodes1), 4)

        option_values1 = [opt_elem.text for opt_elem in option_nodes1]
        self.assertEquals(option_values1,
                          ['Not selected', 'Easy', 'Medium', 'Hard'])

        select_node2 = select_nodes[1]
        self.assertEquals(select_node2.get('name'), self.aside_tag_lo)

        option_nodes2 = select_node2.xpath('option')
        self.assertEquals(len(option_nodes2), 4)

        option_values2 = [
            opt_elem.text for opt_elem in option_nodes2 if opt_elem.text
        ]
        self.assertEquals(option_values2, [
            'Not selected', 'Learned nothing', 'Learned a few things',
            'Learned everything'
        ])

        # Now ensure the acid_aside is not in the result
        self.assertNotRegexpMatches(problem_html,
                                    r"data-block-type=[\"\']acid_aside[\"\']")

        # Ensure about video don't have asides
        video_html = get_preview_fragment(request, self.video, context).content
        self.assertNotRegexpMatches(video_html, "<select")
Exemplo n.º 2
0
    def test_preview_html(self):
        """
        Checks that html for the StructuredTagsAside is generated correctly
        """
        request = RequestFactory().get('/dummy-url')
        request.user = UserFactory()
        request.session = {}

        # Call get_preview_fragment directly.
        context = {
            'reorderable_items': set(),
            'read_only': True
        }
        problem_html = get_preview_fragment(request, self.problem, context).content

        parser = etree.HTMLParser()
        tree = etree.parse(StringIO(problem_html), parser)

        main_div_nodes = tree.xpath('/html/body/div/section/div')
        self.assertEquals(len(main_div_nodes), 1)

        div_node = main_div_nodes[0]
        self.assertEquals(div_node.get('data-init'), 'StructuredTagsInit')
        self.assertEquals(div_node.get('data-runtime-class'), 'PreviewRuntime')
        self.assertEquals(div_node.get('data-block-type'), 'tagging_aside')
        self.assertEquals(div_node.get('data-runtime-version'), '1')
        self.assertIn('xblock_asides-v1', div_node.get('class'))

        select_nodes = div_node.xpath('div/select')
        self.assertEquals(len(select_nodes), 2)

        select_node1 = select_nodes[0]
        self.assertEquals(select_node1.get('name'), self.aside_tag_dif)

        option_nodes1 = select_node1.xpath('option')
        self.assertEquals(len(option_nodes1), 4)

        option_values1 = [opt_elem.text for opt_elem in option_nodes1]
        self.assertEquals(option_values1, ['Not selected', 'Easy', 'Medium', 'Hard'])

        select_node2 = select_nodes[1]
        self.assertEquals(select_node2.get('name'), self.aside_tag_lo)

        option_nodes2 = select_node2.xpath('option')
        self.assertEquals(len(option_nodes2), 4)

        option_values2 = [opt_elem.text for opt_elem in option_nodes2 if opt_elem.text]
        self.assertEquals(option_values2, ['Not selected', 'Learned nothing',
                                           'Learned a few things', 'Learned everything'])

        # Now ensure the acid_aside is not in the result
        self.assertNotRegexpMatches(problem_html, r"data-block-type=[\"\']acid_aside[\"\']")

        # Ensure about video don't have asides
        video_html = get_preview_fragment(request, self.video, context).content
        self.assertNotRegexpMatches(video_html, "<select")
Exemplo n.º 3
0
    def test_preview_fragment(self):
        """
        Test for calling get_preview_html.

        This test used to be specifically about Locators (ensuring that they did not
        get translated to Locations). The test now has questionable value.
        """
        course = CourseFactory.create()
        html = ItemFactory.create(
            parent_location=course.location,
            category="html",
            data={'data': "<html>foobar</html>"}
        )

        request = RequestFactory().get('/dummy-url')
        request.user = UserFactory()
        request.session = {}

        # Call get_preview_fragment directly.
        html = get_preview_fragment(request, html, {}).content

        # Verify student view html is returned, and the usage ID is as expected.
        self.assertRegexpMatches(
            html,
            'data-usage-id="i4x://MITx/999/html/html_[0-9]*"'
        )
        self.assertRegexpMatches(html, '<html>foobar</html>')
Exemplo n.º 4
0
    def test_preview_handler_locator(self):
        """
        Test for calling get_preview_html when descriptor.location is a Locator.
        """
        course = CourseFactory.create()
        html = ItemFactory.create(parent_location=course.location,
                                  category="html",
                                  data={'data': "<html>foobar</html>"})

        locator = loc_mapper().translate_location(course.location.course_id,
                                                  html.location, True, True)

        # Change the stored location to a locator.
        html.location = locator
        html.save()

        request = RequestFactory().get('/dummy-url')
        request.user = UserFactory()
        request.session = {}

        # Must call get_preview_fragment directly, as going through xblock RESTful API will attempt
        # to use item.location as a Location.
        html = get_preview_fragment(request, html, {}).content
        # Verify student view html is returned, and there are no old locations in it.
        self.assertRegexpMatches(
            html,
            'data-usage-id="MITx.999.Robot_Super_Course;_branch;_published;_block;_html_[0-9]*"'
        )
        self.assertRegexpMatches(html, '<html>foobar</html>')
        self.assertNotRegexpMatches(html, 'i4x')
Exemplo n.º 5
0
    def test_preview_no_asides(self):
        """
        Test for calling get_preview_html. Ensures data-usage-id is correctly set and
        asides are correctly excluded because they are not enabled.
        """
        course = CourseFactory.create(default_store=ModuleStoreEnum.Type.split)
        html = ItemFactory.create(
            parent_location=course.location,
            category="html",
            data={'data': "<html>foobar</html>"}
        )

        config = StudioConfig.current()
        config.enabled = False
        config.save()

        request = RequestFactory().get('/dummy-url')
        request.user = UserFactory()
        request.session = {}

        # Call get_preview_fragment directly.
        context = {
            'reorderable_items': set(),
            'read_only': True
        }
        html = get_preview_fragment(request, html, context).content

        self.assertNotRegexpMatches(html, r"data-block-type=[\"\']test_aside[\"\']")
        self.assertNotRegexpMatches(html, "Aside rendered")
Exemplo n.º 6
0
    def test_preview_no_asides(self):
        """
        Test for calling get_preview_html. Ensures data-usage-id is correctly set and
        asides are correctly excluded because they are not enabled.
        """
        course = CourseFactory.create(default_store=ModuleStoreEnum.Type.split)
        html = ItemFactory.create(parent_location=course.location,
                                  category="html",
                                  data={'data': "<html>foobar</html>"})

        config = StudioConfig.current()
        config.enabled = False
        config.save()

        request = RequestFactory().get('/dummy-url')
        request.user = UserFactory()
        request.session = {}

        # Call get_preview_fragment directly.
        context = {'reorderable_items': set(), 'read_only': True}
        html = get_preview_fragment(request, html, context).content

        self.assertNotRegexpMatches(html,
                                    r"data-block-type=[\"\']test_aside[\"\']")
        self.assertNotRegexpMatches(html, "Aside rendered")
Exemplo n.º 7
0
    def test_preview_fragment(self):
        """
        Test for calling get_preview_html.

        This test used to be specifically about Locators (ensuring that they did not
        get translated to Locations). The test now has questionable value.
        """
        course = CourseFactory.create()
        html = ItemFactory.create(
            parent_location=course.location,
            category="html",
            data={'data': "<html>foobar</html>"}
        )

        request = RequestFactory().get('/dummy-url')
        request.user = UserFactory()
        request.session = {}

        # Call get_preview_fragment directly.
        context = {
            'reorderable_items': set(),
            'read_only': True
        }
        html = get_preview_fragment(request, html, context).content

        # Verify student view html is returned, and the usage ID is as expected.
        html_pattern = unicode(course.id.make_usage_key('html', 'html_')).replace('html_', r'html_[0-9]*')
        self.assertRegexpMatches(
            html,
            'data-usage-id="{}"'.format(html_pattern)
        )
        self.assertRegexpMatches(html, '<html>foobar</html>')
Exemplo n.º 8
0
    def test_preview_fragment(self):
        """
        Test for calling get_preview_html.

        This test used to be specifically about Locators (ensuring that they did not
        get translated to Locations). The test now has questionable value.
        """
        course = CourseFactory.create()
        html = ItemFactory.create(parent_location=course.location,
                                  category="html",
                                  data={'data': "<html>foobar</html>"})

        request = RequestFactory().get('/dummy-url')
        request.user = UserFactory()
        request.session = {}

        # Call get_preview_fragment directly.
        context = {'reorderable_items': set(), 'read_only': True}
        html = get_preview_fragment(request, html, context).content

        # Verify student view html is returned, and the usage ID is as expected.
        html_pattern = unicode(course.id.make_usage_key(
            'html', 'html_')).replace('html_', r'html_[0-9]*')
        self.assertRegexpMatches(html,
                                 'data-usage-id="{}"'.format(html_pattern))
        self.assertRegexpMatches(html, '<html>foobar</html>')
Exemplo n.º 9
0
    def test_preview_handler_locator(self):
        """
        Test for calling get_preview_html when descriptor.location is a Locator.
        """
        course = CourseFactory.create()
        html = ItemFactory.create(
            parent_location=course.location,
            category="html",
            data={'data': "<html>foobar</html>"}
        )

        locator = loc_mapper().translate_location(
            course.location.course_id, html.location, True, True
        )

        # Change the stored location to a locator.
        html.location = locator
        html.save()

        request = RequestFactory().get('/dummy-url')
        request.user = UserFactory()
        request.session = {}

        # Must call get_preview_fragment directly, as going through xblock RESTful API will attempt
        # to use item.location as a Location.
        html = get_preview_fragment(request, html).content
        # Verify student view html is returned, and there are no old locations in it.
        self.assertRegexpMatches(
            html,
            'data-usage-id="MITx.999.Robot_Super_Course;_branch;_published;_block;_html_[0-9]*"'
        )
        self.assertRegexpMatches(html, '<html>foobar</html>')
        self.assertNotRegexpMatches(html, 'i4x')
Exemplo n.º 10
0
    def test_preview_fragment(self):
        """
        Test for calling get_preview_html. Ensures data-usage-id is correctly set and
        asides are correctly included.
        """
        course = CourseFactory.create(default_store=ModuleStoreEnum.Type.split)
        html = ItemFactory.create(
            parent_location=course.location,
            category="html",
            data={'data': "<html>foobar</html>"}
        )

        config = StudioConfig.current()
        config.enabled = True
        config.save()

        request = RequestFactory().get('/dummy-url')
        request.user = UserFactory()
        request.session = {}

        # Call get_preview_fragment directly.
        context = {
            'reorderable_items': set(),
            'read_only': True
        }
        html = get_preview_fragment(request, html, context).content

        # Verify student view html is returned, and the usage ID is as expected.
        html_pattern = re.escape(unicode(course.id.make_usage_key('html', 'replaceme'))).replace('replaceme', r'html_[0-9]*')
        self.assertRegexpMatches(
            html,
            'data-usage-id="{}"'.format(html_pattern)
        )
        self.assertRegexpMatches(html, '<html>foobar</html>')
        self.assertRegexpMatches(html, r"data-block-type=[\"\']test_aside[\"\']")
        self.assertRegexpMatches(html, "Aside rendered")
        # Now ensure the acid_aside is not in the result
        self.assertNotRegexpMatches(html, r"data-block-type=[\"\']acid_aside[\"\']")

        # Ensure about pages don't have asides
        about = modulestore().get_item(course.id.make_usage_key('about', 'overview'))
        html = get_preview_fragment(request, about, context).content
        self.assertNotRegexpMatches(html, r"data-block-type=[\"\']test_aside[\"\']")
        self.assertNotRegexpMatches(html, "Aside rendered")
Exemplo n.º 11
0
    def test_preview_fragment(self):
        """
        Test for calling get_preview_html. Ensures data-usage-id is correctly set and
        asides are correctly included.
        """
        course = CourseFactory.create(default_store=ModuleStoreEnum.Type.split)
        html = ItemFactory.create(
            parent_location=course.location,
            category="html",
            data={'data': "<html>foobar</html>"}
        )

        config = StudioConfig.current()
        config.enabled = True
        config.save()

        request = RequestFactory().get('/dummy-url')
        request.user = UserFactory()
        request.session = {}

        # Call get_preview_fragment directly.
        context = {
            'reorderable_items': set(),
            'read_only': True
        }
        html = get_preview_fragment(request, html, context).content

        # Verify student view html is returned, and the usage ID is as expected.
        html_pattern = re.escape(unicode(course.id.make_usage_key('html', 'replaceme'))).replace('replaceme', r'html_[0-9]*')
        self.assertRegexpMatches(
            html,
            'data-usage-id="{}"'.format(html_pattern)
        )
        self.assertRegexpMatches(html, '<html>foobar</html>')
        self.assertRegexpMatches(html, r"data-block-type=[\"\']test_aside[\"\']")
        self.assertRegexpMatches(html, "Aside rendered")
        # Now ensure the acid_aside is not in the result
        self.assertNotRegexpMatches(html, r"data-block-type=[\"\']acid_aside[\"\']")

        # Ensure about pages don't have asides
        about = modulestore().get_item(course.id.make_usage_key('about', 'overview'))
        html = get_preview_fragment(request, about, context).content
        self.assertNotRegexpMatches(html, r"data-block-type=[\"\']test_aside[\"\']")
        self.assertNotRegexpMatches(html, "Aside rendered")
Exemplo n.º 12
0
def xblock_view_handler(request, package_id, view_name, tag=None, branch=None, version_guid=None, block=None):
    """
    The restful handler for requests for rendered xblock views.

    Returns a json object containing two keys:
        html: The rendered html of the view
        resources: A list of tuples where the first element is the resource hash, and
            the second is the resource description
    """
    locator = BlockUsageLocator(package_id=package_id, branch=branch, version_guid=version_guid, block_id=block)
    if not has_course_access(request.user, locator):
        raise PermissionDenied()
    old_location = loc_mapper().translate_locator_to_location(locator)

    accept_header = request.META.get('HTTP_ACCEPT', 'application/json')

    if 'application/x-fragment+json' in accept_header:
        store = get_modulestore(old_location)
        component = store.get_item(old_location)

        # wrap the generated fragment in the xmodule_editor div so that the javascript
        # can bind to it correctly
        component.runtime.wrappers.append(partial(wrap_xblock, 'StudioRuntime'))

        if view_name == 'studio_view':
            try:
                fragment = component.render('studio_view')
            # catch exceptions indiscriminately, since after this point they escape the
            # dungeon and surface as uneditable, unsaveable, and undeletable
            # component-goblins.
            except Exception as exc:                          # pylint: disable=w0703
                log.debug("unable to render studio_view for %r", component, exc_info=True)
                fragment = Fragment(render_to_string('html_error.html', {'message': str(exc)}))

            store.save_xmodule(component)

        elif view_name == 'student_view':
            fragment = get_preview_fragment(request, component)
            fragment.content = render_to_string('component.html', {
                'preview': fragment.content,
                'label': component.display_name or component.scope_ids.block_type,
            })
        else:
            raise Http404

        hashed_resources = OrderedDict()
        for resource in fragment.resources:
            hashed_resources[hash_resource(resource)] = resource

        return JsonResponse({
            'html': fragment.content,
            'resources': hashed_resources.items()
        })

    else:
        return HttpResponse(status=406)
Exemplo n.º 13
0
    def test_preview_html(self):
        """
        Checks that html for the StructuredTagsAside is generated correctly
        """
        request = RequestFactory().get('/dummy-url')
        request.user = UserFactory()
        request.session = {}

        # Call get_preview_fragment directly.
        context = {
            'reorderable_items': set(),
            'read_only': True
        }
        problem_html = get_preview_fragment(request, self.problem, context).content

        parser = etree.HTMLParser()
        tree = etree.parse(StringIO(problem_html), parser)

        main_div_nodes = tree.xpath('/html/body/div/section/div')
        self.assertEquals(len(main_div_nodes), 1)

        div_node = main_div_nodes[0]
        self.assertEquals(div_node.get('data-init'), 'StructuredTagsInit')
        self.assertEquals(div_node.get('data-runtime-class'), 'PreviewRuntime')
        self.assertEquals(div_node.get('data-block-type'), 'tagging_aside')
        self.assertEquals(div_node.get('data-runtime-version'), '1')
        self.assertIn('xblock_asides-v1', div_node.get('class'))

        select_nodes = div_node.xpath('div/select')
        self.assertEquals(len(select_nodes), 1)

        select_node = select_nodes[0]
        self.assertEquals(select_node.get('name'), self.aside_tag)

        # Now ensure the acid_aside is not in the result
        self.assertNotRegexpMatches(problem_html, r"data-block-type=[\"\']acid_aside[\"\']")

        # Ensure about video don't have asides
        video_html = get_preview_fragment(request, self.video, context).content
        self.assertNotRegexpMatches(video_html, "<select")
Exemplo n.º 14
0
    def test_preview_html(self):
        """
        Checks that html for the StructuredTagsAside is generated correctly
        """
        request = RequestFactory().get('/dummy-url')
        request.user = UserFactory()
        request.session = {}

        # Call get_preview_fragment directly.
        context = {'reorderable_items': set(), 'read_only': True}
        problem_html = get_preview_fragment(request, self.problem,
                                            context).content

        parser = etree.HTMLParser()
        tree = etree.parse(StringIO(problem_html), parser)

        main_div_nodes = tree.xpath('/html/body/div/section/div')
        self.assertEquals(len(main_div_nodes), 1)

        div_node = main_div_nodes[0]
        self.assertEquals(div_node.get('data-init'), 'StructuredTagsInit')
        self.assertEquals(div_node.get('data-runtime-class'), 'PreviewRuntime')
        self.assertEquals(div_node.get('data-block-type'), 'tagging_aside')
        self.assertEquals(div_node.get('data-runtime-version'), '1')
        self.assertIn('xblock_asides-v1', div_node.get('class'))

        select_nodes = div_node.xpath('div/select')
        self.assertEquals(len(select_nodes), 1)

        select_node = select_nodes[0]
        self.assertEquals(select_node.get('name'), self.aside_tag)

        # Now ensure the acid_aside is not in the result
        self.assertNotRegexpMatches(problem_html,
                                    r"data-block-type=[\"\']acid_aside[\"\']")

        # Ensure about video don't have asides
        video_html = get_preview_fragment(request, self.video, context).content
        self.assertNotRegexpMatches(video_html, "<select")
Exemplo n.º 15
0
def xblock_view_handler(request, usage_key_string, view_name):
    """
    The restful handler for requests for rendered xblock views.

    Returns a json object containing two keys:
        html: The rendered html of the view
        resources: A list of tuples where the first element is the resource hash, and
            the second is the resource description
    """
    usage_key = usage_key_with_run(usage_key_string)
    if not has_course_access(request.user, usage_key.course_key):
        raise PermissionDenied()

    accept_header = request.META.get('HTTP_ACCEPT', 'application/json')

    if 'application/json' in accept_header:
        store = modulestore()
        xblock = store.get_item(usage_key)
        container_views = ['container_preview', 'reorderable_container_child_preview']

        # wrap the generated fragment in the xmodule_editor div so that the javascript
        # can bind to it correctly
        xblock.runtime.wrappers.append(partial(
            wrap_xblock,
            'StudioRuntime',
            usage_id_serializer=unicode,
            request_token=request_token(request),
        ))

        if view_name == STUDIO_VIEW:
            try:
                fragment = xblock.render(STUDIO_VIEW)
            # catch exceptions indiscriminately, since after this point they escape the
            # dungeon and surface as uneditable, unsaveable, and undeletable
            # component-goblins.
            except Exception as exc:                          # pylint: disable=w0703
                log.debug("unable to render studio_view for %r", xblock, exc_info=True)
                fragment = Fragment(render_to_string('html_error.html', {'message': str(exc)}))

            store.update_item(xblock, request.user.id)
        elif view_name in (PREVIEW_VIEWS + container_views):
            is_pages_view = view_name == STUDENT_VIEW   # Only the "Pages" view uses student view in Studio

            # Determine the items to be shown as reorderable. Note that the view
            # 'reorderable_container_child_preview' is only rendered for xblocks that
            # are being shown in a reorderable container, so the xblock is automatically
            # added to the list.
            reorderable_items = set()
            if view_name == 'reorderable_container_child_preview':
                reorderable_items.add(xblock.location)

            # 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
            }

            fragment = get_preview_fragment(request, xblock, context)

            # Note that the container view recursively adds headers into the preview fragment,
            # so only the "Pages" view requires that this extra wrapper be included.
            if is_pages_view:
                fragment.content = render_to_string('component.html', {
                    'xblock_context': context,
                    'xblock': xblock,
                    'locator': usage_key,
                    'preview': fragment.content,
                    'label': xblock.display_name or xblock.scope_ids.block_type,
                })
        else:
            raise Http404

        hashed_resources = OrderedDict()
        for resource in fragment.resources:
            hashed_resources[hash_resource(resource)] = resource

        return JsonResponse({
            'html': fragment.content,
            'resources': hashed_resources.items()
        })

    else:
        return HttpResponse(status=406)
Exemplo n.º 16
0
def xblock_view_handler(request,
                        package_id,
                        view_name,
                        tag=None,
                        branch=None,
                        version_guid=None,
                        block=None):
    """
    The restful handler for requests for rendered xblock views.

    Returns a json object containing two keys:
        html: The rendered html of the view
        resources: A list of tuples where the first element is the resource hash, and
            the second is the resource description
    """
    locator = BlockUsageLocator(package_id=package_id,
                                branch=branch,
                                version_guid=version_guid,
                                block_id=block)
    if not has_course_access(request.user, locator):
        raise PermissionDenied()
    old_location = loc_mapper().translate_locator_to_location(locator)

    accept_header = request.META.get('HTTP_ACCEPT', 'application/json')

    if 'application/json' in accept_header:
        store = get_modulestore(old_location)
        component = store.get_item(old_location)

        # wrap the generated fragment in the xmodule_editor div so that the javascript
        # can bind to it correctly
        component.runtime.wrappers.append(partial(wrap_xblock,
                                                  'StudioRuntime'))

        if view_name == 'studio_view':
            try:
                fragment = component.render('studio_view')
            # catch exceptions indiscriminately, since after this point they escape the
            # dungeon and surface as uneditable, unsaveable, and undeletable
            # component-goblins.
            except Exception as exc:  # pylint: disable=w0703
                log.debug("unable to render studio_view for %r",
                          component,
                          exc_info=True)
                fragment = Fragment(
                    render_to_string('html_error.html', {'message': str(exc)}))

            # change not authored by requestor but by xblocks.
            store.update_item(component, None)

        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.
            html = render_to_string(
                'container_xblock_component.html', {
                    'xblock': component,
                    'locator': locator,
                    'reordering_enabled': True,
                })
            return JsonResponse({
                'html': html,
                'resources': [],
            })
        elif view_name in ('student_view', 'container_preview'):
            is_container_view = (view_name == 'container_preview')
            component_publish_state = compute_publish_state(component)
            is_read_only_view = component_publish_state == PublishState.public

            # Only show the new style HTML for the container view, i.e. for non-verticals
            # Note: this special case logic can be removed once the unit page is replaced
            # with the new container view.
            context = {
                'runtime_type': 'studio',
                'container_view': is_container_view,
                'read_only': is_read_only_view,
                'root_xblock': component,
            }

            fragment = get_preview_fragment(request, component, context)
            # For old-style pages (such as unit and static pages), wrap the preview with
            # the component div. Note that the container view recursively adds headers
            # into the preview fragment, so we don't want to add another header here.
            if not is_container_view:
                fragment.content = render_to_string(
                    'component.html', {
                        'preview':
                        fragment.content,
                        'label':
                        component.display_name
                        or component.scope_ids.block_type,
                    })
        else:
            raise Http404

        hashed_resources = OrderedDict()
        for resource in fragment.resources:
            hashed_resources[hash_resource(resource)] = resource

        return JsonResponse({
            'html': fragment.content,
            'resources': hashed_resources.items()
        })

    else:
        return HttpResponse(status=406)
Exemplo n.º 17
0
def xblock_view_handler(request, usage_key_string, view_name):
    """
    The restful handler for requests for rendered xblock views.

    Returns a json object containing two keys:
        html: The rendered html of the view
        resources: A list of tuples where the first element is the resource hash, and
            the second is the resource description
    """
    usage_key = usage_key_with_run(usage_key_string)
    if not has_course_author_access(request.user, usage_key.course_key):
        raise PermissionDenied()

    accept_header = request.META.get('HTTP_ACCEPT', 'application/json')

    if 'application/json' in accept_header:
        store = modulestore()
        xblock = store.get_item(usage_key)
        container_views = [
            'container_preview', 'reorderable_container_child_preview'
        ]

        # wrap the generated fragment in the xmodule_editor div so that the javascript
        # can bind to it correctly
        xblock.runtime.wrappers.append(
            partial(
                wrap_xblock,
                'StudioRuntime',
                usage_id_serializer=unicode,
                request_token=request_token(request),
            ))

        if view_name == STUDIO_VIEW:
            try:
                fragment = xblock.render(STUDIO_VIEW)
            # catch exceptions indiscriminately, since after this point they escape the
            # dungeon and surface as uneditable, unsaveable, and undeletable
            # component-goblins.
            except Exception as exc:  # pylint: disable=broad-except
                log.debug("unable to render studio_view for %r",
                          xblock,
                          exc_info=True)
                fragment = Fragment(
                    render_to_string('html_error.html', {'message': str(exc)}))

        elif view_name in (PREVIEW_VIEWS + container_views):
            is_pages_view = view_name == STUDENT_VIEW  # Only the "Pages" view uses student view in Studio

            # Determine the items to be shown as reorderable. Note that the view
            # 'reorderable_container_child_preview' is only rendered for xblocks that
            # are being shown in a reorderable container, so the xblock is automatically
            # added to the list.
            reorderable_items = set()
            if view_name == 'reorderable_container_child_preview':
                reorderable_items.add(xblock.location)

            # 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
            }

            fragment = get_preview_fragment(request, xblock, context)

            # Note that the container view recursively adds headers into the preview fragment,
            # so only the "Pages" view requires that this extra wrapper be included.
            if is_pages_view:
                fragment.content = render_to_string(
                    'component.html', {
                        'xblock_context':
                        context,
                        'xblock':
                        xblock,
                        'locator':
                        usage_key,
                        'preview':
                        fragment.content,
                        'label':
                        xblock.display_name or xblock.scope_ids.block_type,
                    })
        else:
            raise Http404

        hashed_resources = OrderedDict()
        for resource in fragment.resources:
            hashed_resources[hash_resource(resource)] = resource

        return JsonResponse({
            'html': fragment.content,
            'resources': hashed_resources.items()
        })

    else:
        return HttpResponse(status=406)
Exemplo n.º 18
0
def xblock_view_handler(request, usage_key_string, view_name):
    """
    The restful handler for requests for rendered xblock views.

    Returns a json object containing two keys:
        html: The rendered html of the view
        resources: A list of tuples where the first element is the resource hash, and
            the second is the resource description
    """
    usage_key = usage_key_with_run(usage_key_string)
    if not has_studio_read_access(request.user, usage_key.course_key):
        raise PermissionDenied()

    accept_header = request.META.get("HTTP_ACCEPT", "application/json")

    if "application/json" in accept_header:
        store = modulestore()
        xblock = store.get_item(usage_key)
        container_views = ["container_preview", "reorderable_container_child_preview", "container_child_preview"]

        # wrap the generated fragment in the xmodule_editor div so that the javascript
        # can bind to it correctly
        xblock.runtime.wrappers.append(
            partial(wrap_xblock, "StudioRuntime", usage_id_serializer=unicode, request_token=request_token(request))
        )

        if view_name in (STUDIO_VIEW, VISIBILITY_VIEW):
            try:
                fragment = xblock.render(view_name)
            # catch exceptions indiscriminately, since after this point they escape the
            # dungeon and surface as uneditable, unsaveable, and undeletable
            # component-goblins.
            except Exception as exc:  # pylint: disable=broad-except
                log.debug("Unable to render %s for %r", view_name, xblock, exc_info=True)
                fragment = Fragment(render_to_string("html_error.html", {"message": str(exc)}))

        elif view_name in PREVIEW_VIEWS + container_views:
            is_pages_view = view_name == STUDENT_VIEW  # Only the "Pages" view uses student view in Studio
            can_edit = has_studio_write_access(request.user, usage_key.course_key)

            # Determine the items to be shown as reorderable. Note that the view
            # 'reorderable_container_child_preview' is only rendered for xblocks that
            # are being shown in a reorderable container, so the xblock is automatically
            # added to the list.
            reorderable_items = set()
            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:
                # pylint: disable=too-many-format-args
                return HttpResponse(
                    content="Couldn't parse paging parameters: enable_paging: "
                    "{0}, page_number: {1}, page_size: {2}".format(
                        request.REQUEST.get("enable_paging", "false"),
                        request.REQUEST.get("page_number", 0),
                        request.REQUEST.get("page_size", 0),
                    ),
                    status=400,
                    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.
            context = {
                "is_pages_view": is_pages_view,  # This setting disables the recursive wrapping of xblocks
                "is_unit_page": is_unit(xblock),
                "can_edit": can_edit,
                "root_xblock": xblock if (view_name == "container_preview") else None,
                "reorderable_items": reorderable_items,
                "paging": paging,
                "force_render": force_render,
            }

            fragment = get_preview_fragment(request, xblock, context)

            # Note that the container view recursively adds headers into the preview fragment,
            # so only the "Pages" view requires that this extra wrapper be included.
            if is_pages_view:
                fragment.content = render_to_string(
                    "component.html",
                    {
                        "xblock_context": context,
                        "xblock": xblock,
                        "locator": usage_key,
                        "preview": fragment.content,
                        "label": xblock.display_name or xblock.scope_ids.block_type,
                    },
                )
        else:
            raise Http404

        hashed_resources = OrderedDict()
        for resource in fragment.resources:
            hashed_resources[hash_resource(resource)] = resource

        return JsonResponse({"html": fragment.content, "resources": hashed_resources.items()})

    else:
        return HttpResponse(status=406)
Exemplo n.º 19
0
def xblock_view_handler(request, package_id, view_name, tag=None, branch=None, version_guid=None, block=None):
    """
    The restful handler for requests for rendered xblock views.

    Returns a json object containing two keys:
        html: The rendered html of the view
        resources: A list of tuples where the first element is the resource hash, and
            the second is the resource description
    """
    locator = BlockUsageLocator(package_id=package_id, branch=branch, version_guid=version_guid, block_id=block)
    if not has_course_access(request.user, locator):
        raise PermissionDenied()
    old_location = loc_mapper().translate_locator_to_location(locator)

    accept_header = request.META.get('HTTP_ACCEPT', 'application/json')

    if 'application/json' in accept_header:
        store = get_modulestore(old_location)
        component = store.get_item(old_location)

        # wrap the generated fragment in the xmodule_editor div so that the javascript
        # can bind to it correctly
        component.runtime.wrappers.append(partial(wrap_xblock, 'StudioRuntime'))

        if view_name == 'studio_view':
            try:
                fragment = component.render('studio_view')
            # catch exceptions indiscriminately, since after this point they escape the
            # dungeon and surface as uneditable, unsaveable, and undeletable
            # component-goblins.
            except Exception as exc:                          # pylint: disable=w0703
                log.debug("unable to render studio_view for %r", component, exc_info=True)
                fragment = Fragment(render_to_string('html_error.html', {'message': str(exc)}))

            # change not authored by requestor but by xblocks.
            store.update_item(component, None)

        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.
            html = render_to_string('container_xblock_component.html', {
                'xblock': component,
                'locator': locator,
                'reordering_enabled': True,
            })
            return JsonResponse({
                'html': html,
                'resources': [],
            })
        elif view_name in ('student_view', 'container_preview'):
            is_container_view = (view_name == 'container_preview')

            # Only show the new style HTML for the container view, i.e. for non-verticals
            # Note: this special case logic can be removed once the unit page is replaced
            # with the new container view.
            is_read_only_view = is_container_view
            context = {
                'container_view': is_container_view,
                'read_only': is_read_only_view,
                'root_xblock': component
            }

            fragment = get_preview_fragment(request, component, context)
            # For old-style pages (such as unit and static pages), wrap the preview with
            # the component div. Note that the container view recursively adds headers
            # into the preview fragment, so we don't want to add another header here.
            if not is_container_view:
                fragment.content = render_to_string('component.html', {
                    'preview': fragment.content,
                    'label': component.display_name or component.scope_ids.block_type,

                    # Native XBlocks are responsible for persisting their own data,
                    # so they are also responsible for providing save/cancel buttons.
                    'show_save_cancel': isinstance(component, xmodule.x_module.XModuleDescriptor),
                })
        else:
            raise Http404

        hashed_resources = OrderedDict()
        for resource in fragment.resources:
            hashed_resources[hash_resource(resource)] = resource

        return JsonResponse({
            'html': fragment.content,
            'resources': hashed_resources.items()
        })

    else:
        return HttpResponse(status=406)
Exemplo n.º 20
0
def xblock_view_handler(request, usage_key_string, view_name):
    """
    The restful handler for requests for rendered xblock views.

    Returns a json object containing two keys:
        html: The rendered html of the view
        resources: A list of tuples where the first element is the resource hash, and
            the second is the resource description
    """
    usage_key = UsageKey.from_string(usage_key_string)
    if not has_course_access(request.user, usage_key.course_key):
        raise PermissionDenied()

    accept_header = request.META.get('HTTP_ACCEPT', 'application/json')

    if 'application/json' in accept_header:
        store = modulestore()
        xblock = store.get_item(usage_key)
        is_read_only = _is_xblock_read_only(xblock)
        container_views = ['container_preview', 'reorderable_container_child_preview']
        unit_views = PREVIEW_VIEWS

        # wrap the generated fragment in the xmodule_editor div so that the javascript
        # can bind to it correctly
        xblock.runtime.wrappers.append(partial(wrap_xblock, 'StudioRuntime', usage_id_serializer=unicode))

        if view_name == STUDIO_VIEW:
            try:
                fragment = xblock.render(STUDIO_VIEW)
            # catch exceptions indiscriminately, since after this point they escape the
            # dungeon and surface as uneditable, unsaveable, and undeletable
            # component-goblins.
            except Exception as exc:                          # pylint: disable=w0703
                log.debug("unable to render studio_view for %r", xblock, exc_info=True)
                fragment = Fragment(render_to_string('html_error.html', {'message': str(exc)}))

            store.update_item(xblock, request.user.id)
        elif view_name in (unit_views + container_views):
            is_container_view = (view_name in container_views)

            # Determine the items to be shown as reorderable. Note that the view
            # 'reorderable_container_child_preview' is only rendered for xblocks that
            # are being shown in a reorderable container, so the xblock is automatically
            # added to the list.
            reorderable_items = set()
            if view_name == 'reorderable_container_child_preview':
                reorderable_items.add(xblock.location)

            # Only show the new style HTML for the container view, i.e. for non-verticals
            # Note: this special case logic can be removed once the unit page is replaced
            # with the new container view.
            context = {
                'container_view': is_container_view,
                'read_only': is_read_only,
                'root_xblock': xblock if (view_name == 'container_preview') else None,
                'reorderable_items': reorderable_items
            }

            fragment = get_preview_fragment(request, xblock, context)
            # For old-style pages (such as unit and static pages), wrap the preview with
            # the component div. Note that the container view recursively adds headers
            # into the preview fragment, so we don't want to add another header here.
            if not is_container_view:
                # For non-leaf xblocks, show the special rendering which links to the new container page.
                if xblock_has_own_studio_page(xblock):
                    template = 'container_xblock_component.html'
                else:
                    template = 'component.html'
                fragment.content = render_to_string(template, {
                    'xblock_context': context,
                    'xblock': xblock,
                    'locator': usage_key,
                    'preview': fragment.content,
                    'label': xblock.display_name or xblock.scope_ids.block_type,
                })
        else:
            raise Http404

        hashed_resources = OrderedDict()
        for resource in fragment.resources:
            hashed_resources[hash_resource(resource)] = resource

        return JsonResponse({
            'html': fragment.content,
            'resources': hashed_resources.items()
        })

    else:
        return HttpResponse(status=406)
Exemplo n.º 21
0
def xblock_view_handler(request, usage_key_string, view_name):
    """
    The restful handler for requests for rendered xblock views.

    Returns a json object containing two keys:
        html: The rendered html of the view
        resources: A list of tuples where the first element is the resource hash, and
            the second is the resource description
    """
    usage_key = UsageKey.from_string(usage_key_string)
    if not has_course_access(request.user, usage_key.course_key):
        raise PermissionDenied()

    accept_header = request.META.get('HTTP_ACCEPT', 'application/json')

    if 'application/json' in accept_header:
        store = get_modulestore(usage_key)
        xblock = store.get_item(usage_key)
        is_read_only = _is_xblock_read_only(xblock)
        container_views = [
            'container_preview', 'reorderable_container_child_preview'
        ]
        unit_views = ['student_view']

        # wrap the generated fragment in the xmodule_editor div so that the javascript
        # can bind to it correctly
        xblock.runtime.wrappers.append(
            partial(wrap_xblock, 'StudioRuntime', usage_id_serializer=unicode))

        if view_name == 'studio_view':
            try:
                fragment = xblock.render('studio_view')
            # catch exceptions indiscriminately, since after this point they escape the
            # dungeon and surface as uneditable, unsaveable, and undeletable
            # component-goblins.
            except Exception as exc:  # pylint: disable=w0703
                log.debug("unable to render studio_view for %r",
                          xblock,
                          exc_info=True)
                fragment = Fragment(
                    render_to_string('html_error.html', {'message': str(exc)}))

            # change not authored by requestor but by xblocks.
            store.update_item(xblock, None)
        elif view_name in (unit_views + container_views):
            is_container_view = (view_name in container_views)

            # Determine the items to be shown as reorderable. Note that the view
            # 'reorderable_container_child_preview' is only rendered for xblocks that
            # are being shown in a reorderable container, so the xblock is automatically
            # added to the list.
            reorderable_items = set()
            if view_name == 'reorderable_container_child_preview':
                reorderable_items.add(xblock.location)

            # Only show the new style HTML for the container view, i.e. for non-verticals
            # Note: this special case logic can be removed once the unit page is replaced
            # with the new container view.
            context = {
                'runtime_type': 'studio',
                'container_view': is_container_view,
                'read_only': is_read_only,
                'root_xblock': xblock if
                (view_name == 'container_preview') else None,
                'reorderable_items': reorderable_items
            }

            fragment = get_preview_fragment(request, xblock, context)
            # For old-style pages (such as unit and static pages), wrap the preview with
            # the component div. Note that the container view recursively adds headers
            # into the preview fragment, so we don't want to add another header here.
            if not is_container_view:
                # For non-leaf xblocks, show the special rendering which links to the new container page.
                if xblock_has_own_studio_page(xblock):
                    template = 'container_xblock_component.html'
                else:
                    template = 'component.html'
                fragment.content = render_to_string(
                    template, {
                        'xblock_context':
                        context,
                        'xblock':
                        xblock,
                        'locator':
                        usage_key,
                        'preview':
                        fragment.content,
                        'label':
                        xblock.display_name or xblock.scope_ids.block_type,
                    })
        else:
            raise Http404

        hashed_resources = OrderedDict()
        for resource in fragment.resources:
            hashed_resources[hash_resource(resource)] = resource

        return JsonResponse({
            'html': fragment.content,
            'resources': hashed_resources.items()
        })

    else:
        return HttpResponse(status=406)
Exemplo n.º 22
0
def xblock_handler(request, tag=None, package_id=None, branch=None, version_guid=None, block=None):
    """
    The restful handler for xblock requests.

    DELETE
        json: delete this xblock instance from the course. Supports query parameters "recurse" to delete
        all children and "all_versions" to delete from all (mongo) versions.
    GET
        json: returns representation of the xblock (locator id, data, and metadata).
              if ?fields=graderType, it returns the graderType for the unit instead of the above.
        html: returns HTML for rendering the xblock (which includes both the "preview" view and the "editor" view)
    PUT or POST
        json: if xblock locator is specified, update the xblock instance. The json payload can contain
              these fields, all optional:
                :data: the new value for the data.
                :children: the locator ids of children for this xblock.
                :metadata: new values for the metadata fields. Any whose values are None will be deleted not set
                       to None! Absent ones will be left alone.
                :nullout: which metadata fields to set to None
                :graderType: change how this unit is graded
                :publish: can be one of three values, 'make_public, 'make_private', or 'create_draft'
              The JSON representation on the updated xblock (minus children) is returned.

              if xblock locator is not specified, create a new xblock instance, either by duplicating
              an existing xblock, or creating an entirely new one. The json playload can contain
              these fields:
                :parent_locator: parent for new xblock, required for both duplicate and create new instance
                :duplicate_source_locator: if present, use this as the source for creating a duplicate copy
                :category: type of xblock, required if duplicate_source_locator is not present.
                :display_name: name for new xblock, optional
                :boilerplate: template name for populating fields, optional and only used
                     if duplicate_source_locator is not present
              The locator (and old-style id) for the created xblock (minus children) is returned.
    """
    if package_id is not None:
        locator = BlockUsageLocator(package_id=package_id, branch=branch, version_guid=version_guid, block_id=block)
        if not has_course_access(request.user, locator):
            raise PermissionDenied()
        old_location = loc_mapper().translate_locator_to_location(locator)

        if request.method == 'GET':
            accept_header = request.META.get('HTTP_ACCEPT', 'application/json')

            if 'application/x-fragment+json' in accept_header:
                component = modulestore().get_item(old_location)

                # Wrap the generated fragment in the xmodule_editor div so that the javascript
                # can bind to it correctly
                component.runtime.wrappers.append(partial(wrap_xblock, 'StudioRuntime'))

                try:
                    editor_fragment = component.render('studio_view')
                # catch exceptions indiscriminately, since after this point they escape the
                # dungeon and surface as uneditable, unsaveable, and undeletable
                # component-goblins.
                except Exception as exc:                          # pylint: disable=W0703
                    log.debug("Unable to render studio_view for %r", component, exc_info=True)
                    editor_fragment = Fragment(render_to_string('html_error.html', {'message': str(exc)}))

                modulestore().save_xmodule(component)

                preview_fragment = get_preview_fragment(request, component)

                hashed_resources = OrderedDict()
                for resource in editor_fragment.resources + preview_fragment.resources:
                    hashed_resources[hash_resource(resource)] = resource

                return JsonResponse({
                    'html': render_to_string('component.html', {
                        'preview': preview_fragment.content,
                        'editor': editor_fragment.content,
                        'label': component.display_name or component.scope_ids.block_type,
                    }),
                    'resources': hashed_resources.items()
                })

            elif 'application/json' in accept_header:
                fields = request.REQUEST.get('fields', '').split(',')
                if 'graderType' in fields:
                    # right now can't combine output of this w/ output of _get_module_info, but worthy goal
                    return JsonResponse(CourseGradingModel.get_section_grader_type(locator))
                # TODO: pass fields to _get_module_info and only return those
                rsp = _get_module_info(locator)
                return JsonResponse(rsp)
            else:
                return HttpResponse(status=406)

        elif request.method == 'DELETE':
            delete_children = str_to_bool(request.REQUEST.get('recurse', 'False'))
            delete_all_versions = str_to_bool(request.REQUEST.get('all_versions', 'False'))

            return _delete_item_at_location(old_location, delete_children, delete_all_versions)
        else:  # Since we have a package_id, we are updating an existing xblock.
            return _save_item(
                request,
                locator,
                old_location,
                data=request.json.get('data'),
                children=request.json.get('children'),
                metadata=request.json.get('metadata'),
                nullout=request.json.get('nullout'),
                grader_type=request.json.get('graderType'),
                publish=request.json.get('publish'),
            )
    elif request.method in ('PUT', 'POST'):
        if 'duplicate_source_locator' in request.json:
            parent_locator = BlockUsageLocator(request.json['parent_locator'])
            duplicate_source_locator = BlockUsageLocator(request.json['duplicate_source_locator'])

            # _duplicate_item is dealing with locations to facilitate the recursive call for
            # duplicating children.
            parent_location = loc_mapper().translate_locator_to_location(parent_locator)
            duplicate_source_location = loc_mapper().translate_locator_to_location(duplicate_source_locator)
            dest_location = _duplicate_item(
                parent_location,
                duplicate_source_location,
                request.json.get('display_name')
            )
            course_location = loc_mapper().translate_locator_to_location(BlockUsageLocator(parent_locator), get_course=True)
            dest_locator = loc_mapper().translate_location(course_location.course_id, dest_location, False, True)
            return JsonResponse({"locator": unicode(dest_locator)})
        else:
            return _create_item(request)
    else:
        return HttpResponseBadRequest(
            "Only instance creation is supported without a package_id.",
            content_type="text/plain"
        )