Beispiel #1
0
def container_handler(request, tag=None, package_id=None, branch=None, version_guid=None, block=None):
    """
    The restful handler for container xblock requests.

    GET
        html: returns the HTML page for editing a container
        json: not currently supported
    """
    if 'text/html' in request.META.get('HTTP_ACCEPT', 'text/html'):
        locator = BlockUsageLocator(package_id=package_id, branch=branch, version_guid=version_guid, block_id=block)
        try:
            old_location, course, xblock, __ = _get_item_in_course(request, locator)
        except ItemNotFoundError:
            return HttpResponseBadRequest()

        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,
            'ancestor_xblocks': ancestor_xblocks,
        })
    else:
        return HttpResponseBadRequest("Only supports html requests")
Beispiel #2
0
def _create_xblock_ancestor_info(xblock, course_outline):
    """
    Returns information about the ancestors of an xblock. Note that the direct parent will also return
    information about all of its children.
    """
    ancestors = []

    def collect_ancestor_info(ancestor, include_child_info=False):
        """
        Collect xblock info regarding the specified xblock and its ancestors.
        """
        if ancestor:
            direct_children_only = lambda parent: parent == ancestor
            ancestors.append(
                create_xblock_info(
                    ancestor,
                    include_child_info=include_child_info,
                    course_outline=course_outline,
                    include_children_predicate=direct_children_only,
                )
            )
            collect_ancestor_info(get_parent_xblock(ancestor))

    collect_ancestor_info(get_parent_xblock(xblock), include_child_info=True)
    return {"ancestors": ancestors}
Beispiel #3
0
def subsection_handler(request, usage_key_string):
    """
    The restful handler for subsection-specific requests.

    GET
        html: return html page for editing a subsection
        json: not currently supported
    """
    if 'text/html' in request.META.get('HTTP_ACCEPT', 'text/html'):
        usage_key = UsageKey.from_string(usage_key_string)
        try:
            course, item, lms_link = _get_item_in_course(request, usage_key)
        except ItemNotFoundError:
            return HttpResponseBadRequest()

        preview_link = get_lms_link_for_item(item.location, preview=True)

        # make sure that location references a 'sequential', otherwise return
        # BadRequest
        if item.location.category != 'sequential':
            return HttpResponseBadRequest()

        parent = get_parent_xblock(item)

        # remove all metadata from the generic dictionary that is presented in a
        # more normalized UI. We only want to display the XBlocks fields, not
        # the fields from any mixins that have been added
        fields = getattr(item, 'unmixed_class', item.__class__).fields

        policy_metadata = dict(
            (field.name, field.read_from(item))
            for field
            in fields.values()
            if field.name not in ['display_name', 'start', 'due', 'format'] and field.scope == Scope.settings
        )

        can_view_live = False
        subsection_units = item.get_children()
        can_view_live = any([modulestore().has_published_version(unit) for unit in subsection_units])

        return render_to_response(
            'edit_subsection.html',
            {
                'subsection': item,
                'context_course': course,
                'new_unit_category': 'vertical',
                'lms_link': lms_link,
                'preview_link': preview_link,
                'course_graders': json.dumps(CourseGradingModel.fetch(item.location.course_key).graders),
                'parent_item': parent,
                'locator': item.location,
                'policy_metadata': policy_metadata,
                'subsection_units': subsection_units,
                'can_view_live': can_view_live
            }
        )
    else:
        return HttpResponseBadRequest("Only supports html requests")
Beispiel #4
0
def container_handler(request,
                      tag=None,
                      package_id=None,
                      branch=None,
                      version_guid=None,
                      block=None):
    """
    The restful handler for container xblock requests.

    GET
        html: returns the HTML page for editing a container
        json: not currently supported
    """
    if 'text/html' in request.META.get('HTTP_ACCEPT', 'text/html'):
        locator = BlockUsageLocator(package_id=package_id,
                                    branch=branch,
                                    version_guid=version_guid,
                                    block_id=block)
        try:
            __, course, xblock, __ = _get_item_in_course(request, locator)
        except ItemNotFoundError:
            return HttpResponseBadRequest()

        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()

        unit = ancestor_xblocks[0] if ancestor_xblocks else None
        unit_publish_state = compute_publish_state(unit) if unit else None

        return render_to_response(
            'container.html', {
                'context_course': course,
                'xblock': xblock,
                'xblock_locator': locator,
                'unit': unit,
                'unit_publish_state': unit_publish_state,
                'ancestor_xblocks': ancestor_xblocks,
            })
    else:
        return HttpResponseBadRequest("Only supports html requests")
Beispiel #5
0
def container_handler(request, usage_key_string):
    """
    The restful handler for container xblock requests.

    GET
        html: returns the HTML page for editing a container
        json: not currently supported
    """
    if 'text/html' in request.META.get('HTTP_ACCEPT', 'text/html'):

        usage_key = UsageKey.from_string(usage_key_string)
        try:
            course, xblock, __ = _get_item_in_course(request, usage_key)
        except ItemNotFoundError:
            return HttpResponseBadRequest()

        component_templates = _get_component_templates(course)
        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()

        unit = ancestor_xblocks[0] if ancestor_xblocks else None
        unit_publish_state = compute_publish_state(unit) if unit else None

        return render_to_response(
            'container.html',
            {
                'context_course':
                course,  # Needed only for display of menus at top of page.
                'xblock': xblock,
                'unit_publish_state': unit_publish_state,
                'xblock_locator': usage_key,
                'unit': None if not ancestor_xblocks else ancestor_xblocks[0],
                'ancestor_xblocks': ancestor_xblocks,
                'component_templates': json.dumps(component_templates),
            })
    else:
        return HttpResponseBadRequest("Only supports html requests")
Beispiel #6
0
 def collect_ancestor_info(ancestor, include_child_info=False):
     """
     Collect xblock info regarding the specified xblock and its ancestors.
     """
     if ancestor:
         direct_children_only = lambda parent: parent == ancestor
         ancestors.append(create_xblock_info(
             ancestor,
             include_child_info=include_child_info,
             course_outline=course_outline,
             include_children_predicate=direct_children_only
         ))
         collect_ancestor_info(get_parent_xblock(ancestor))
Beispiel #7
0
 def collect_ancestor_info(ancestor, include_child_info=False):
     """
     Collect xblock info regarding the specified xblock and its ancestors.
     """
     if ancestor:
         direct_children_only = lambda parent: parent == ancestor
         ancestors.append(
             create_xblock_info(
                 ancestor,
                 include_child_info=include_child_info,
                 course_outline=course_outline,
                 include_children_predicate=direct_children_only))
         collect_ancestor_info(get_parent_xblock(ancestor))
Beispiel #8
0
def container_handler(request, usage_key_string):
    """
    The restful handler for container xblock requests.

    GET
        html: returns the HTML page for editing a container
        json: not currently supported
    """
    if 'text/html' in request.META.get('HTTP_ACCEPT', 'text/html'):

        usage_key = UsageKey.from_string(usage_key_string)
        try:
            course, xblock, __ = _get_item_in_course(request, usage_key)
        except ItemNotFoundError:
            return HttpResponseBadRequest()

        component_templates = _get_component_templates(course)
        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()

        unit = ancestor_xblocks[0] if ancestor_xblocks else None
        unit_publish_state = compute_publish_state(unit) if unit else None

        return render_to_response('container.html', {
            'context_course': course,  # Needed only for display of menus at top of page.
            'xblock': xblock,
            'unit_publish_state': unit_publish_state,
            'xblock_locator': usage_key,
            'unit': None if not ancestor_xblocks else ancestor_xblocks[0],
            'ancestor_xblocks': ancestor_xblocks,
            'component_templates': json.dumps(component_templates),
        })
    else:
        return HttpResponseBadRequest("Only supports html requests")
Beispiel #9
0
def container_handler(request, tag=None, package_id=None, branch=None, version_guid=None, block=None):
    """
    The restful handler for container xblock requests.

    GET
        html: returns the HTML page for editing a container
        json: not currently supported
    """
    if "text/html" in request.META.get("HTTP_ACCEPT", "text/html"):
        locator = BlockUsageLocator(package_id=package_id, branch=branch, version_guid=version_guid, block_id=block)
        try:
            old_location, course, xblock, __ = _get_item_in_course(request, locator)
        except ItemNotFoundError:
            return HttpResponseBadRequest()
        parent_xblock = get_parent_xblock(xblock)

        return render_to_response(
            "container.html",
            {"context_course": course, "xblock": xblock, "xblock_locator": locator, "parent_xblock": parent_xblock},
        )
    else:
        return HttpResponseBadRequest("Only supports html requests")
Beispiel #10
0
def _create_xblock_ancestor_info(xblock, course_outline):
    """
    Returns information about the ancestors of an xblock. Note that the direct parent will also return
    information about all of its children.
    """
    ancestors = []

    def collect_ancestor_info(ancestor, include_child_info=False):
        """
        Collect xblock info regarding the specified xblock and its ancestors.
        """
        if ancestor:
            direct_children_only = lambda parent: parent == ancestor
            ancestors.append(
                create_xblock_info(
                    ancestor,
                    include_child_info=include_child_info,
                    course_outline=course_outline,
                    include_children_predicate=direct_children_only))
            collect_ancestor_info(get_parent_xblock(ancestor))

    collect_ancestor_info(get_parent_xblock(xblock), include_child_info=True)
    return {'ancestors': ancestors}
Beispiel #11
0
def container_handler(request, tag=None, package_id=None, branch=None, version_guid=None, block=None):
    """
    The restful handler for container xblock requests.

    GET
        html: returns the HTML page for editing a container
        json: not currently supported
    """
    if 'text/html' in request.META.get('HTTP_ACCEPT', 'text/html'):
        locator = BlockUsageLocator(package_id=package_id, branch=branch, version_guid=version_guid, block_id=block)
        try:
            old_location, course, xblock, __ = _get_item_in_course(request, locator)
        except ItemNotFoundError:
            return HttpResponseBadRequest()
        parent_xblock = get_parent_xblock(xblock)

        return render_to_response('container.html', {
            'context_course': course,
            'xblock': xblock,
            'xblock_locator': locator,
            'parent_xblock': parent_xblock,
        })
    else:
        return HttpResponseBadRequest("Only supports html requests")
Beispiel #12
0
def unit_handler(request, usage_key_string):
    """
    The restful handler for unit-specific requests.

    GET
        html: return html page for editing a unit
        json: not currently supported
    """
    if 'text/html' in request.META.get('HTTP_ACCEPT', 'text/html'):
        usage_key = UsageKey.from_string(usage_key_string)
        try:
            course, item, lms_link = _get_item_in_course(request, usage_key)
        except ItemNotFoundError:
            return HttpResponseBadRequest()

        component_templates = _get_component_templates(course)

        xblocks = item.get_children()

        # TODO (cpennington): If we share units between courses,
        # this will need to change to check permissions correctly so as
        # to pick the correct parent subsection
        containing_subsection = get_parent_xblock(item)
        containing_section = get_parent_xblock(containing_subsection)

        # cdodge hack. We're having trouble previewing drafts via jump_to redirect
        # so let's generate the link url here

        # need to figure out where this item is in the list of children as the
        # preview will need this
        index = 1
        for child in containing_subsection.get_children():
            if child.location == item.location:
                break
            index = index + 1

        preview_lms_base = settings.FEATURES.get('PREVIEW_LMS_BASE')

        preview_lms_link = (
            u'//{preview_lms_base}/courses/{org}/{course}/{course_name}/courseware/{section}/{subsection}/{index}'
        ).format(
            preview_lms_base=preview_lms_base,
            lms_base=settings.LMS_BASE,
            org=course.location.org,
            course=course.location.course,
            course_name=course.location.name,
            section=containing_section.location.name,
            subsection=containing_subsection.location.name,
            index=index
        )

        return render_to_response('unit.html', {
            'context_course': course,
            'unit': item,
            'unit_usage_key': usage_key,
            'child_usage_keys': [block.scope_ids.usage_id for block in xblocks],
            'component_templates': json.dumps(component_templates),
            'draft_preview_link': preview_lms_link,
            'published_preview_link': lms_link,
            'subsection': containing_subsection,
            'release_date': (
                get_default_time_display(containing_subsection.start)
                if containing_subsection.start is not None else None
            ),
            'section': containing_section,
            'new_unit_category': 'vertical',
            'unit_state': compute_publish_state(item),
            'published_date': (
                get_default_time_display(item.published_date)
                if item.published_date is not None else None
            ),
        })
    else:
        return HttpResponseBadRequest("Only supports html requests")
Beispiel #13
0
def subsection_handler(request, tag=None, package_id=None, branch=None, version_guid=None, block=None):
    """
    The restful handler for subsection-specific requests.

    GET
        html: return html page for editing a subsection
        json: not currently supported
    """
    if "text/html" in request.META.get("HTTP_ACCEPT", "text/html"):
        locator = BlockUsageLocator(package_id=package_id, branch=branch, version_guid=version_guid, block_id=block)
        try:
            old_location, course, item, lms_link = _get_item_in_course(request, locator)
        except ItemNotFoundError:
            return HttpResponseBadRequest()

        preview_link = get_lms_link_for_item(old_location, course_id=course.location.course_id, preview=True)

        # make sure that location references a 'sequential', otherwise return
        # BadRequest
        if item.location.category != "sequential":
            return HttpResponseBadRequest()

        parent = get_parent_xblock(item)

        # remove all metadata from the generic dictionary that is presented in a
        # more normalized UI. We only want to display the XBlocks fields, not
        # the fields from any mixins that have been added
        fields = getattr(item, "unmixed_class", item.__class__).fields

        policy_metadata = dict(
            (field.name, field.read_from(item))
            for field in fields.values()
            if field.name not in ["display_name", "start", "due", "format"] and field.scope == Scope.settings
        )

        can_view_live = False
        subsection_units = item.get_children()
        for unit in subsection_units:
            state = compute_unit_state(unit)
            if state == UnitState.public or state == UnitState.draft:
                can_view_live = True
                break

        course_locator = loc_mapper().translate_location(course.location.course_id, course.location, False, True)

        return render_to_response(
            "edit_subsection.html",
            {
                "subsection": item,
                "context_course": course,
                "new_unit_category": "vertical",
                "lms_link": lms_link,
                "preview_link": preview_link,
                "course_graders": json.dumps(CourseGradingModel.fetch(course_locator).graders),
                "parent_item": parent,
                "locator": locator,
                "policy_metadata": policy_metadata,
                "subsection_units": subsection_units,
                "can_view_live": can_view_live,
            },
        )
    else:
        return HttpResponseBadRequest("Only supports html requests")
Beispiel #14
0
def unit_handler(request, tag=None, package_id=None, branch=None, version_guid=None, block=None):
    """
    The restful handler for unit-specific requests.

    GET
        html: return html page for editing a unit
        json: not currently supported
    """
    if "text/html" in request.META.get("HTTP_ACCEPT", "text/html"):
        locator = BlockUsageLocator(package_id=package_id, branch=branch, version_guid=version_guid, block_id=block)
        try:
            old_location, course, item, lms_link = _get_item_in_course(request, locator)
        except ItemNotFoundError:
            return HttpResponseBadRequest()

        component_templates = defaultdict(list)
        for category in COMPONENT_TYPES:
            component_class = _load_mixed_class(category)
            # add the default template
            # TODO: Once mixins are defined per-application, rather than per-runtime,
            # this should use a cms mixed-in class. (cpennington)
            if hasattr(component_class, "display_name"):
                display_name = component_class.display_name.default or "Blank"
            else:
                display_name = "Blank"
            component_templates[category].append(
                (
                    display_name,
                    category,
                    False,  # No defaults have markdown (hardcoded current default)
                    None,  # no boilerplate for overrides
                )
            )
            # add boilerplates
            if hasattr(component_class, "templates"):
                for template in component_class.templates():
                    filter_templates = getattr(component_class, "filter_templates", None)
                    if not filter_templates or filter_templates(template, course):
                        component_templates[category].append(
                            (
                                template["metadata"].get("display_name"),
                                category,
                                template["metadata"].get("markdown") is not None,
                                template.get("template_id"),
                            )
                        )

        # Check if there are any advanced modules specified in the course policy.
        # These modules should be specified as a list of strings, where the strings
        # are the names of the modules in ADVANCED_COMPONENT_TYPES that should be
        # enabled for the course.
        course_advanced_keys = course.advanced_modules

        # Set component types according to course policy file
        if isinstance(course_advanced_keys, list):
            for category in course_advanced_keys:
                if category in ADVANCED_COMPONENT_TYPES:
                    # Do I need to allow for boilerplates or just defaults on the
                    # class? i.e., can an advanced have more than one entry in the
                    # menu? one for default and others for prefilled boilerplates?
                    try:
                        component_class = _load_mixed_class(category)

                        component_templates["advanced"].append(
                            (
                                component_class.display_name.default or category,
                                category,
                                False,
                                None,  # don't override default data
                            )
                        )
                    except PluginMissingError:
                        # dhm: I got this once but it can happen any time the
                        # course author configures an advanced component which does
                        # not exist on the server. This code here merely
                        # prevents any authors from trying to instantiate the
                        # non-existent component type by not showing it in the menu
                        pass
        else:
            log.error("Improper format for course advanced keys! %s", course_advanced_keys)

        xblocks = item.get_children()
        locators = [
            loc_mapper().translate_location(course.location.course_id, xblock.location, False, True)
            for xblock in xblocks
        ]

        # TODO (cpennington): If we share units between courses,
        # this will need to change to check permissions correctly so as
        # to pick the correct parent subsection
        containing_subsection = get_parent_xblock(item)
        containing_section = get_parent_xblock(containing_subsection)

        # cdodge hack. We're having trouble previewing drafts via jump_to redirect
        # so let's generate the link url here

        # need to figure out where this item is in the list of children as the
        # preview will need this
        index = 1
        for child in containing_subsection.get_children():
            if child.location == item.location:
                break
            index = index + 1

        preview_lms_base = settings.FEATURES.get("PREVIEW_LMS_BASE")

        preview_lms_link = (
            u"//{preview_lms_base}/courses/{org}/{course}/{course_name}/courseware/{section}/{subsection}/{index}"
        ).format(
            preview_lms_base=preview_lms_base,
            lms_base=settings.LMS_BASE,
            org=course.location.org,
            course=course.location.course,
            course_name=course.location.name,
            section=containing_section.location.name,
            subsection=containing_subsection.location.name,
            index=index,
        )

        return render_to_response(
            "unit.html",
            {
                "context_course": course,
                "unit": item,
                "unit_locator": locator,
                "locators": locators,
                "component_templates": component_templates,
                "draft_preview_link": preview_lms_link,
                "published_preview_link": lms_link,
                "subsection": containing_subsection,
                "release_date": (
                    get_default_time_display(containing_subsection.start)
                    if containing_subsection.start is not None
                    else None
                ),
                "section": containing_section,
                "new_unit_category": "vertical",
                "unit_state": compute_unit_state(item),
                "published_date": (
                    get_default_time_display(item.published_date) if item.published_date is not None else None
                ),
            },
        )
    else:
        return HttpResponseBadRequest("Only supports html requests")
Beispiel #15
0
def container_handler(request, usage_key_string):
    """
    The restful handler for container xblock requests.

    GET
        html: returns the HTML page for editing a container
        json: not currently supported
    """
    if 'text/html' in request.META.get('HTTP_ACCEPT', 'text/html'):

        usage_key = UsageKey.from_string(usage_key_string)
        try:
            course, xblock, lms_link = _get_item_in_course(request, usage_key)
        except ItemNotFoundError:
            return HttpResponseBadRequest()

        component_templates = get_component_templates(course)
        ancestor_xblocks = []
        parent = get_parent_xblock(xblock)
        action = request.REQUEST.get('action', 'view')

        is_unit_page = is_unit(xblock)
        unit = xblock if is_unit_page else None

        while parent and parent.category != 'course':
            if unit is None and is_unit(parent):
                unit = parent
            ancestor_xblocks.append(parent)
            parent = get_parent_xblock(parent)
        ancestor_xblocks.reverse()

        assert unit is not None, "Could not determine unit page"
        subsection = get_parent_xblock(unit)
        assert subsection is not None, "Could not determine parent subsection from unit " + unicode(unit.location)
        section = get_parent_xblock(subsection)
        assert section is not None, "Could not determine ancestor section from unit " + unicode(unit.location)

        # Fetch the XBlock info for use by the container page. Note that it includes information
        # about the block's ancestors and siblings for use by the Unit Outline.
        xblock_info = create_xblock_info(xblock, include_ancestor_info=is_unit_page)

        # Create the link for preview.
        preview_lms_base = settings.FEATURES.get('PREVIEW_LMS_BASE')
        # need to figure out where this item is in the list of children as the
        # preview will need this
        index = 1
        for child in subsection.get_children():
            if child.location == unit.location:
                break
            index += 1
        preview_lms_link = (
            u'//{preview_lms_base}/courses/{org}/{course}/{course_name}/courseware/{section}/{subsection}/{index}'
        ).format(
            preview_lms_base=preview_lms_base,
            lms_base=settings.LMS_BASE,
            org=course.location.org,
            course=course.location.course,
            course_name=course.location.name,
            section=section.location.name,
            subsection=subsection.location.name,
            index=index
        )

        return render_to_response('container.html', {
            'context_course': course,  # Needed only for display of menus at top of page.
            'action': action,
            'xblock': xblock,
            'xblock_locator': xblock.location,
            'unit': unit,
            'is_unit_page': is_unit_page,
            'subsection': subsection,
            'section': section,
            'new_unit_category': 'vertical',
            'ancestor_xblocks': ancestor_xblocks,
            'component_templates': json.dumps(component_templates),
            'xblock_info': xblock_info,
            'draft_preview_link': preview_lms_link,
            'published_preview_link': lms_link,
        })
    else:
        return HttpResponseBadRequest("Only supports HTML requests")
Beispiel #16
0
def unit_handler(request,
                 tag=None,
                 package_id=None,
                 branch=None,
                 version_guid=None,
                 block=None):
    """
    The restful handler for unit-specific requests.

    GET
        html: return html page for editing a unit
        json: not currently supported
    """
    if 'text/html' in request.META.get('HTTP_ACCEPT', 'text/html'):
        locator = BlockUsageLocator(package_id=package_id,
                                    branch=branch,
                                    version_guid=version_guid,
                                    block_id=block)
        try:
            old_location, course, item, lms_link = _get_item_in_course(
                request, locator)
        except ItemNotFoundError:
            return HttpResponseBadRequest()

        component_templates = defaultdict(list)
        for category in COMPONENT_TYPES:
            component_class = _load_mixed_class(category)
            # add the default template
            # TODO: Once mixins are defined per-application, rather than per-runtime,
            # this should use a cms mixed-in class. (cpennington)
            if hasattr(component_class, 'display_name'):
                display_name = component_class.display_name.default or 'Blank'
            else:
                display_name = 'Blank'
            component_templates[category].append((
                display_name,
                category,
                False,  # No defaults have markdown (hardcoded current default)
                None  # no boilerplate for overrides
            ))
            # add boilerplates
            if hasattr(component_class, 'templates'):
                for template in component_class.templates():
                    filter_templates = getattr(component_class,
                                               'filter_templates', None)
                    if not filter_templates or filter_templates(
                            template, course):
                        component_templates[category].append(
                            (template['metadata'].get('display_name'),
                             category, template['metadata'].get('markdown')
                             is not None, template.get('template_id')))

        # Check if there are any advanced modules specified in the course policy.
        # These modules should be specified as a list of strings, where the strings
        # are the names of the modules in ADVANCED_COMPONENT_TYPES that should be
        # enabled for the course.
        course_advanced_keys = course.advanced_modules

        # Set component types according to course policy file
        if isinstance(course_advanced_keys, list):
            for category in course_advanced_keys:
                if category in ADVANCED_COMPONENT_TYPES:
                    # Do I need to allow for boilerplates or just defaults on the
                    # class? i.e., can an advanced have more than one entry in the
                    # menu? one for default and others for prefilled boilerplates?
                    try:
                        component_class = _load_mixed_class(category)

                        component_templates['advanced'].append((
                            component_class.display_name.default or category,
                            category,
                            False,
                            None  # don't override default data
                        ))
                    except PluginMissingError:
                        # dhm: I got this once but it can happen any time the
                        # course author configures an advanced component which does
                        # not exist on the server. This code here merely
                        # prevents any authors from trying to instantiate the
                        # non-existent component type by not showing it in the menu
                        pass
        else:
            log.error("Improper format for course advanced keys! %s",
                      course_advanced_keys)

        xblocks = item.get_children()
        locators = [
            loc_mapper().translate_location(course.location.course_id,
                                            xblock.location, False, True)
            for xblock in xblocks
        ]

        # TODO (cpennington): If we share units between courses,
        # this will need to change to check permissions correctly so as
        # to pick the correct parent subsection
        containing_subsection = get_parent_xblock(item)
        containing_section = get_parent_xblock(containing_subsection)

        # cdodge hack. We're having trouble previewing drafts via jump_to redirect
        # so let's generate the link url here

        # need to figure out where this item is in the list of children as the
        # preview will need this
        index = 1
        for child in containing_subsection.get_children():
            if child.location == item.location:
                break
            index = index + 1

        preview_lms_base = settings.FEATURES.get('PREVIEW_LMS_BASE')

        preview_lms_link = (
            u'//{preview_lms_base}/courses/{org}/{course}/{course_name}/courseware/{section}/{subsection}/{index}'
        ).format(preview_lms_base=preview_lms_base,
                 lms_base=settings.LMS_BASE,
                 org=course.location.org,
                 course=course.location.course,
                 course_name=course.location.name,
                 section=containing_section.location.name,
                 subsection=containing_subsection.location.name,
                 index=index)

        return render_to_response(
            'unit.html', {
                'context_course':
                course,
                'unit':
                item,
                'unit_locator':
                locator,
                'locators':
                locators,
                'component_templates':
                component_templates,
                'draft_preview_link':
                preview_lms_link,
                'published_preview_link':
                lms_link,
                'subsection':
                containing_subsection,
                'release_date':
                (get_default_time_display(containing_subsection.start)
                 if containing_subsection.start is not None else None),
                'section':
                containing_section,
                'new_unit_category':
                'vertical',
                'unit_state':
                compute_publish_state(item),
                'published_date':
                (get_default_time_display(item.published_date)
                 if item.published_date is not None else None),
            })
    else:
        return HttpResponseBadRequest("Only supports html requests")
Beispiel #17
0
def container_handler(request, usage_key_string):
    """
    The restful handler for container xblock requests.

    GET
        html: returns the HTML page for editing a container
        json: not currently supported
    """
    if 'text/html' in request.META.get('HTTP_ACCEPT', 'text/html'):

        usage_key = UsageKey.from_string(usage_key_string)
        try:
            course, xblock, lms_link = _get_item_in_course(request, usage_key)
        except ItemNotFoundError:
            return HttpResponseBadRequest()

        component_templates = get_component_templates(course)
        ancestor_xblocks = []
        parent = get_parent_xblock(xblock)
        action = request.REQUEST.get('action', 'view')

        is_unit_page = is_unit(xblock)
        unit = xblock if is_unit_page else None

        while parent and parent.category != 'course':
            if unit is None and is_unit(parent):
                unit = parent
            ancestor_xblocks.append(parent)
            parent = get_parent_xblock(parent)
        ancestor_xblocks.reverse()

        assert unit is not None, "Could not determine unit page"
        subsection = get_parent_xblock(unit)
        assert subsection is not None, "Could not determine parent subsection from unit " + unicode(unit.location)
        section = get_parent_xblock(subsection)
        assert section is not None, "Could not determine ancestor section from unit " + unicode(unit.location)

        # Fetch the XBlock info for use by the container page. Note that it includes information
        # about the block's ancestors and siblings for use by the Unit Outline.
        xblock_info = create_xblock_info(xblock, include_ancestor_info=is_unit_page)

        # Create the link for preview.
        preview_lms_base = settings.FEATURES.get('PREVIEW_LMS_BASE')
        # need to figure out where this item is in the list of children as the
        # preview will need this
        index = 1
        for child in subsection.get_children():
            if child.location == unit.location:
                break
            index += 1
        preview_lms_link = (
            u'//{preview_lms_base}/courses/{org}/{course}/{course_name}/courseware/{section}/{subsection}/{index}'
        ).format(
            preview_lms_base=preview_lms_base,
            lms_base=settings.LMS_BASE,
            org=course.location.org,
            course=course.location.course,
            course_name=course.location.name,
            section=section.location.name,
            subsection=subsection.location.name,
            index=index
        )

        return render_to_response('container.html', {
            'context_course': course,  # Needed only for display of menus at top of page.
            'action': action,
            'xblock': xblock,
            'xblock_locator': xblock.location,
            'unit': unit,
            'is_unit_page': is_unit_page,
            'subsection': subsection,
            'section': section,
            'new_unit_category': 'vertical',
            'ancestor_xblocks': ancestor_xblocks,
            'component_templates': json.dumps(component_templates),
            'xblock_info': xblock_info,
            'draft_preview_link': preview_lms_link,
            'published_preview_link': lms_link,
        })
    else:
        return HttpResponseBadRequest("Only supports HTML requests")
Beispiel #18
0
def container_handler(request, usage_key_string):
    """
    The restful handler for container xblock requests.

    GET
        html: returns the HTML page for editing a container
        json: not currently supported
    """
    if 'text/html' in request.META.get('HTTP_ACCEPT', 'text/html'):

        try:
            usage_key = UsageKey.from_string(usage_key_string)
        except InvalidKeyError:  # Raise Http404 on invalid 'usage_key_string'
            raise Http404
        with modulestore().bulk_operations(usage_key.course_key):
            try:
                course, xblock, lms_link, preview_lms_link = _get_item_in_course(request, usage_key)
            except ItemNotFoundError:
                return HttpResponseBadRequest()

            component_templates = get_component_templates(course)
            ancestor_xblocks = []
            parent = get_parent_xblock(xblock)
            action = request.REQUEST.get('action', 'view')

            is_unit_page = is_unit(xblock)
            unit = xblock if is_unit_page else None

            while parent and parent.category != 'course':
                if unit is None and is_unit(parent):
                    unit = parent
                ancestor_xblocks.append(parent)
                parent = get_parent_xblock(parent)
            ancestor_xblocks.reverse()

            assert unit is not None, "Could not determine unit page"
            subsection = get_parent_xblock(unit)
            assert subsection is not None, "Could not determine parent subsection from unit " + unicode(unit.location)
            section = get_parent_xblock(subsection)
            assert section is not None, "Could not determine ancestor section from unit " + unicode(unit.location)

            # Fetch the XBlock info for use by the container page. Note that it includes information
            # about the block's ancestors and siblings for use by the Unit Outline.
            xblock_info = create_xblock_info(xblock, include_ancestor_info=is_unit_page)

            if is_unit_page:
                add_container_page_publishing_info(xblock, xblock_info)

            # need to figure out where this item is in the list of children as the
            # preview will need this
            index = 1
            for child in subsection.get_children():
                if child.location == unit.location:
                    break
                index += 1

            return render_to_response('container.html', {
                'context_course': course,  # Needed only for display of menus at top of page.
                'action': action,
                'xblock': xblock,
                'xblock_locator': xblock.location,
                'unit': unit,
                'is_unit_page': is_unit_page,
                'subsection': subsection,
                'section': section,
                'new_unit_category': 'vertical',
                'ancestor_xblocks': ancestor_xblocks,
                'component_templates': json.dumps(component_templates),
                'xblock_info': xblock_info,
                'draft_preview_link': preview_lms_link,
                'published_preview_link': lms_link,
                'keywords_supported': get_keywords_supported(),
                'templates': CONTAINER_TEMPATES
            })
    else:
        return HttpResponseBadRequest("Only supports HTML requests")
Beispiel #19
0
def unit_handler(request, usage_key_string):
    """
    The restful handler for unit-specific requests.

    GET
        html: return html page for editing a unit
        json: not currently supported
    """
    if 'text/html' in request.META.get('HTTP_ACCEPT', 'text/html'):
        usage_key = UsageKey.from_string(usage_key_string)
        try:
            course, item, lms_link = _get_item_in_course(request, usage_key)
        except ItemNotFoundError:
            return HttpResponseBadRequest()

        component_templates = _get_component_templates(course)

        xblocks = item.get_children()

        # TODO (cpennington): If we share units between courses,
        # this will need to change to check permissions correctly so as
        # to pick the correct parent subsection
        containing_subsection = get_parent_xblock(item)
        containing_section = get_parent_xblock(containing_subsection)

        # cdodge hack. We're having trouble previewing drafts via jump_to redirect
        # so let's generate the link url here

        # need to figure out where this item is in the list of children as the
        # preview will need this
        index = 1
        for child in containing_subsection.get_children():
            if child.location == item.location:
                break
            index = index + 1

        preview_lms_base = settings.FEATURES.get('PREVIEW_LMS_BASE')

        preview_lms_link = (
            u'//{preview_lms_base}/courses/{org}/{course}/{course_name}/courseware/{section}/{subsection}/{index}'
        ).format(
            preview_lms_base=preview_lms_base,
            lms_base=settings.LMS_BASE,
            org=course.location.org,
            course=course.location.course,
            course_name=course.location.name,
            section=containing_section.location.name,
            subsection=containing_subsection.location.name,
            index=index
        )

        return render_to_response('unit.html', {
            'context_course': course,
            'unit': item,
            'unit_usage_key': usage_key,
            'child_usage_keys': [block.scope_ids.usage_id for block in xblocks],
            'component_templates': json.dumps(component_templates),
            'draft_preview_link': preview_lms_link,
            'published_preview_link': lms_link,
            'subsection': containing_subsection,
            'release_date': (
                get_default_time_display(containing_subsection.start)
                if containing_subsection.start is not None else None
            ),
            'section': containing_section,
            'new_unit_category': 'vertical',
            'unit_state': compute_publish_state(item),
            'published_date': (
                get_default_time_display(item.published_date)
                if item.published_date is not None else None
            ),
        })
    else:
        return HttpResponseBadRequest("Only supports html requests")
Beispiel #20
0
def subsection_handler(request,
                       tag=None,
                       package_id=None,
                       branch=None,
                       version_guid=None,
                       block=None):
    """
    The restful handler for subsection-specific requests.

    GET
        html: return html page for editing a subsection
        json: not currently supported
    """
    if 'text/html' in request.META.get('HTTP_ACCEPT', 'text/html'):
        locator = BlockUsageLocator(package_id=package_id,
                                    branch=branch,
                                    version_guid=version_guid,
                                    block_id=block)
        try:
            old_location, course, item, lms_link = _get_item_in_course(
                request, locator)
        except ItemNotFoundError:
            return HttpResponseBadRequest()

        preview_link = get_lms_link_for_item(
            old_location, course_id=course.location.course_id, preview=True)

        # make sure that location references a 'sequential', otherwise return
        # BadRequest
        if item.location.category != 'sequential':
            return HttpResponseBadRequest()

        parent = get_parent_xblock(item)

        # remove all metadata from the generic dictionary that is presented in a
        # more normalized UI. We only want to display the XBlocks fields, not
        # the fields from any mixins that have been added
        fields = getattr(item, 'unmixed_class', item.__class__).fields

        policy_metadata = dict(
            (field.name, field.read_from(item)) for field in fields.values()
            if field.name not in ['display_name', 'start', 'due', 'format']
            and field.scope == Scope.settings)

        can_view_live = False
        subsection_units = item.get_children()
        for unit in subsection_units:
            state = compute_publish_state(unit)
            if state in (PublishState.public, PublishState.draft):
                can_view_live = True
                break

        course_locator = loc_mapper().translate_location(
            course.location.course_id, course.location, False, True)

        return render_to_response(
            'edit_subsection.html', {
                'subsection':
                item,
                'context_course':
                course,
                'new_unit_category':
                'vertical',
                'lms_link':
                lms_link,
                'preview_link':
                preview_link,
                'course_graders':
                json.dumps(CourseGradingModel.fetch(course_locator).graders),
                'parent_item':
                parent,
                'locator':
                locator,
                'policy_metadata':
                policy_metadata,
                'subsection_units':
                subsection_units,
                'can_view_live':
                can_view_live
            })
    else:
        return HttpResponseBadRequest("Only supports html requests")
Beispiel #21
0
def container_handler(request, usage_key_string):
    """
    The restful handler for container xblock requests.

    GET
        html: returns the HTML page for editing a container
        json: not currently supported
    """
    if 'text/html' in request.META.get('HTTP_ACCEPT', 'text/html'):

        try:
            usage_key = UsageKey.from_string(usage_key_string)
        except InvalidKeyError:  # Raise Http404 on invalid 'usage_key_string'
            raise Http404
        with modulestore().bulk_operations(usage_key.course_key):
            try:
                course, xblock, lms_link, preview_lms_link = _get_item_in_course(
                    request, usage_key)
            except ItemNotFoundError:
                return HttpResponseBadRequest()

            component_templates = get_component_templates(course)
            ancestor_xblocks = []
            parent = get_parent_xblock(xblock)
            action = request.GET.get('action', 'view')

            is_unit_page = is_unit(xblock)
            unit = xblock if is_unit_page else None

            while parent and parent.category != 'course':
                if unit is None and is_unit(parent):
                    unit = parent
                ancestor_xblocks.append(parent)
                parent = get_parent_xblock(parent)
            ancestor_xblocks.reverse()

            assert unit is not None, "Could not determine unit page"
            subsection = get_parent_xblock(unit)
            assert subsection is not None, "Could not determine parent subsection from unit " + unicode(
                unit.location)
            section = get_parent_xblock(subsection)
            assert section is not None, "Could not determine ancestor section from unit " + unicode(
                unit.location)

            # Fetch the XBlock info for use by the container page. Note that it includes information
            # about the block's ancestors and siblings for use by the Unit Outline.
            xblock_info = create_xblock_info(
                xblock, include_ancestor_info=is_unit_page)

            if is_unit_page:
                add_container_page_publishing_info(xblock, xblock_info)

            # need to figure out where this item is in the list of children as the
            # preview will need this
            index = 1
            for child in subsection.get_children():
                if child.location == unit.location:
                    break
                index += 1

            return render_to_response(
                'container.html',
                {
                    'context_course':
                    course,  # Needed only for display of menus at top of page.
                    'action': action,
                    'xblock': xblock,
                    'xblock_locator': xblock.location,
                    'unit': unit,
                    'is_unit_page': is_unit_page,
                    'subsection': subsection,
                    'section': section,
                    'new_unit_category': 'vertical',
                    'ancestor_xblocks': ancestor_xblocks,
                    'component_templates': component_templates,
                    'xblock_info': xblock_info,
                    'draft_preview_link': preview_lms_link,
                    'published_preview_link': lms_link,
                    'templates': CONTAINER_TEMPLATES
                })
    else:
        return HttpResponseBadRequest("Only supports HTML requests")
Beispiel #22
0
def create_xblock_info(xblock,
                       data=None,
                       metadata=None,
                       include_ancestor_info=False,
                       include_child_info=False,
                       course_outline=False,
                       include_children_predicate=NEVER,
                       parent_xblock=None,
                       graders=None):
    """
    Creates the information needed for client-side XBlockInfo.

    If data or metadata are not specified, their information will not be added
    (regardless of whether or not the xblock actually has data or metadata).

    There are three optional boolean parameters:
      include_ancestor_info - if true, ancestor info is added to the response
      include_child_info - if true, direct child info is included in the response
      course_outline - if true, the xblock is being rendered on behalf of the course outline.
        There are certain expensive computations that do not need to be included in this case.

    In addition, an optional include_children_predicate argument can be provided to define whether or
    not a particular xblock should have its children included.
    """
    is_library_block = isinstance(xblock.location, LibraryUsageLocator)
    is_xblock_unit = is_unit(xblock, parent_xblock)
    # this should not be calculated for Sections and Subsections on Unit page or for library blocks
    has_changes = None
    if (is_xblock_unit or course_outline) and not is_library_block:
        has_changes = modulestore().has_changes(xblock)

    if graders is None:
        if not is_library_block:
            graders = CourseGradingModel.fetch(
                xblock.location.course_key).graders
        else:
            graders = []

    # Filter the graders data as needed
    graders = _filter_entrance_exam_grader(graders)

    # Compute the child info first so it can be included in aggregate information for the parent
    should_visit_children = include_child_info and (
        course_outline and not is_xblock_unit or not course_outline)
    if should_visit_children and xblock.has_children:
        child_info = _create_xblock_child_info(
            xblock,
            course_outline,
            graders,
            include_children_predicate=include_children_predicate,
        )
    else:
        child_info = None

    if xblock.category != 'course':
        visibility_state = _compute_visibility_state(
            xblock, child_info, is_xblock_unit and has_changes)
    else:
        visibility_state = None
    published = modulestore().has_published_version(
        xblock) if not is_library_block else None

    # defining the default value 'True' for delete, drag and add new child actions in xblock_actions for each xblock.
    xblock_actions = {
        'deletable': True,
        'draggable': True,
        'childAddable': True
    }
    explanatory_message = None
    # is_entrance_exam is inherited metadata.
    if xblock.category == 'chapter' and getattr(xblock, "is_entrance_exam",
                                                None):
        # Entrance exam section should not be deletable, draggable and not have 'New Subsection' button.
        xblock_actions['deletable'] = xblock_actions[
            'childAddable'] = xblock_actions['draggable'] = False
        if parent_xblock is None:
            parent_xblock = get_parent_xblock(xblock)

        explanatory_message = _(
            'Students must score {score}% or higher to access course materials.'
        ).format(score=int(parent_xblock.entrance_exam_minimum_score_pct *
                           100))

    xblock_info = {
        "id":
        unicode(xblock.location),
        "display_name":
        xblock.display_name_with_default,
        "category":
        xblock.category,
        "edited_on":
        get_default_time_display(xblock.subtree_edited_on)
        if xblock.subtree_edited_on else None,
        "published":
        published,
        "published_on":
        get_default_time_display(xblock.published_on)
        if published and xblock.published_on else None,
        "studio_url":
        xblock_studio_url(xblock, parent_xblock),
        "released_to_students":
        datetime.now(UTC) > xblock.start,
        "release_date":
        _get_release_date(xblock),
        "visibility_state":
        visibility_state,
        "has_explicit_staff_lock":
        xblock.fields['visible_to_staff_only'].is_set_on(xblock),
        "start":
        xblock.fields['start'].to_json(xblock.start),
        "graded":
        xblock.graded,
        "due_date":
        get_default_time_display(xblock.due),
        "due":
        xblock.fields['due'].to_json(xblock.due),
        "format":
        xblock.format,
        "course_graders":
        json.dumps([grader.get('type') for grader in graders]),
        "has_changes":
        has_changes,
        "actions":
        xblock_actions,
        "explanatory_message":
        explanatory_message
    }

    # Entrance exam subsection should be hidden. in_entrance_exam is inherited metadata, all children will have it.
    if xblock.category == 'sequential' and getattr(xblock, "in_entrance_exam",
                                                   False):
        xblock_info["is_header_visible"] = False

    if data is not None:
        xblock_info["data"] = data
    if metadata is not None:
        xblock_info["metadata"] = metadata
    if include_ancestor_info:
        xblock_info['ancestor_info'] = _create_xblock_ancestor_info(
            xblock, course_outline)
    if child_info:
        xblock_info['child_info'] = child_info
    if visibility_state == VisibilityState.staff_only:
        xblock_info["ancestor_has_staff_lock"] = ancestor_has_staff_lock(
            xblock, parent_xblock)
    else:
        xblock_info["ancestor_has_staff_lock"] = False

    if course_outline:
        if xblock_info["has_explicit_staff_lock"]:
            xblock_info["staff_only_message"] = True
        elif child_info and child_info["children"]:
            xblock_info["staff_only_message"] = all([
                child["staff_only_message"] for child in child_info["children"]
            ])
        else:
            xblock_info["staff_only_message"] = False

    return xblock_info
Beispiel #23
0
def container_handler(request, usage_key_string):
    """
    The restful handler for container xblock requests.

    GET
        html: returns the HTML page for editing a container
        json: not currently supported
    """
    if 'text/html' in request.META.get('HTTP_ACCEPT', 'text/html'):

        try:
            usage_key = UsageKey.from_string(usage_key_string)
        except InvalidKeyError:  # Raise Http404 on invalid 'usage_key_string'
            raise Http404
        with modulestore().bulk_operations(usage_key.course_key):
            try:
                course, xblock, lms_link, preview_lms_link = _get_item_in_course(
                    request, usage_key)
            except ItemNotFoundError:
                return HttpResponseBadRequest()

            component_templates = get_component_templates(course)
            ancestor_xblocks = []
            parent = get_parent_xblock(xblock)
            action = request.GET.get('action', 'view')

            is_unit_page = is_unit(xblock)
            unit = xblock if is_unit_page else None

            is_first = True
            while parent:
                if unit is None and is_unit(parent):
                    unit = parent
                elif parent.category != 'sequential':
                    current_block = {
                        'block': parent,
                        'children': parent.get_children(),
                        'is_last': is_first
                    }
                    is_first = False
                    ancestor_xblocks.append(current_block)
                parent = get_parent_xblock(parent)

            ancestor_xblocks.reverse()

            assert unit is not None, "Could not determine unit page"
            subsection = get_parent_xblock(unit)
            assert subsection is not None, "Could not determine parent subsection from unit " + six.text_type(
                unit.location)
            section = get_parent_xblock(subsection)
            assert section is not None, "Could not determine ancestor section from unit " + six.text_type(
                unit.location)

            # for the sequence navigator
            prev_url, next_url = get_sibling_urls(subsection)
            # these are quoted here because they'll end up in a query string on the page,
            # and quoting with mako will trigger the xss linter...
            prev_url = quote_plus(prev_url) if prev_url else None
            next_url = quote_plus(next_url) if next_url else None

            # Fetch the XBlock info for use by the container page. Note that it includes information
            # about the block's ancestors and siblings for use by the Unit Outline.
            xblock_info = create_xblock_info(
                xblock, include_ancestor_info=is_unit_page)

            if is_unit_page:
                add_container_page_publishing_info(xblock, xblock_info)

            # need to figure out where this item is in the list of children as the
            # preview will need this
            index = 1
            for child in subsection.get_children():
                if child.location == unit.location:
                    break
                index += 1

            return render_to_response(
                'container.html',
                {
                    'language_code':
                    request.LANGUAGE_CODE,
                    'context_course':
                    course,  # Needed only for display of menus at top of page.
                    'action':
                    action,
                    'xblock':
                    xblock,
                    'xblock_locator':
                    xblock.location,
                    'unit':
                    unit,
                    'is_unit_page':
                    is_unit_page,
                    'subsection':
                    subsection,
                    'section':
                    section,
                    'position':
                    index,
                    'prev_url':
                    prev_url,
                    'next_url':
                    next_url,
                    'new_unit_category':
                    'vertical',
                    'outline_url':
                    '{url}?format=concise'.format(
                        url=reverse_course_url('course_handler', course.id)),
                    'ancestor_xblocks':
                    ancestor_xblocks,
                    'component_templates':
                    component_templates,
                    'xblock_info':
                    xblock_info,
                    'draft_preview_link':
                    preview_lms_link,
                    'published_preview_link':
                    lms_link,
                    'templates':
                    CONTAINER_TEMPLATES
                })
    else:
        return HttpResponseBadRequest("Only supports HTML requests")
Beispiel #24
0
def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=False, include_child_info=False,
                       course_outline=False, include_children_predicate=NEVER, parent_xblock=None, graders=None,
                       user=None):
    """
    Creates the information needed for client-side XBlockInfo.

    If data or metadata are not specified, their information will not be added
    (regardless of whether or not the xblock actually has data or metadata).

    There are three optional boolean parameters:
      include_ancestor_info - if true, ancestor info is added to the response
      include_child_info - if true, direct child info is included in the response
      course_outline - if true, the xblock is being rendered on behalf of the course outline.
        There are certain expensive computations that do not need to be included in this case.

    In addition, an optional include_children_predicate argument can be provided to define whether or
    not a particular xblock should have its children included.
    """
    is_library_block = isinstance(xblock.location, LibraryUsageLocator)
    is_xblock_unit = is_unit(xblock, parent_xblock)
    # this should not be calculated for Sections and Subsections on Unit page or for library blocks
    has_changes = None
    if (is_xblock_unit or course_outline) and not is_library_block:
        has_changes = modulestore().has_changes(xblock)

    if graders is None:
        if not is_library_block:
            graders = CourseGradingModel.fetch(xblock.location.course_key).graders
        else:
            graders = []

    # Filter the graders data as needed
    graders = _filter_entrance_exam_grader(graders)

    # Compute the child info first so it can be included in aggregate information for the parent
    should_visit_children = include_child_info and (course_outline and not is_xblock_unit or not course_outline)
    if should_visit_children and xblock.has_children:
        child_info = _create_xblock_child_info(
            xblock,
            course_outline,
            graders,
            include_children_predicate=include_children_predicate,
            user=user
        )
    else:
        child_info = None

    if xblock.category != 'course':
        visibility_state = _compute_visibility_state(xblock, child_info, is_xblock_unit and has_changes)
    else:
        visibility_state = None
    published = modulestore().has_published_version(xblock) if not is_library_block else None

    # defining the default value 'True' for delete, drag and add new child actions in xblock_actions for each xblock.
    xblock_actions = {'deletable': True, 'draggable': True, 'childAddable': True}
    explanatory_message = None
    # is_entrance_exam is inherited metadata.
    if xblock.category == 'chapter' and getattr(xblock, "is_entrance_exam", None):
        # Entrance exam section should not be deletable, draggable and not have 'New Subsection' button.
        xblock_actions['deletable'] = xblock_actions['childAddable'] = xblock_actions['draggable'] = False
        if parent_xblock is None:
            parent_xblock = get_parent_xblock(xblock)

        # Translators: The {pct_sign} here represents the percent sign, i.e., '%'
        # in many languages. This is used to avoid Transifex's misinterpreting of
        # '% o'. The percent sign is also translatable as a standalone string.
        explanatory_message = _('Students must score {score}{pct_sign} or higher to access course materials.').format(
            score=int(parent_xblock.entrance_exam_minimum_score_pct * 100),
            # Translators: This is the percent sign. It will be used to represent
            # a percent value out of 100, e.g. "58%" means "58/100".
            pct_sign=_('%'))

    xblock_info = {
        "id": unicode(xblock.location),
        "display_name": xblock.display_name_with_default,
        "category": xblock.category,
        "edited_on": get_default_time_display(xblock.subtree_edited_on) if xblock.subtree_edited_on else None,
        "published": published,
        "published_on": get_default_time_display(xblock.published_on) if published and xblock.published_on else None,
        "studio_url": xblock_studio_url(xblock, parent_xblock),
        "released_to_students": datetime.now(UTC) > xblock.start,
        "release_date": _get_release_date(xblock, user),
        "visibility_state": visibility_state,
        "has_explicit_staff_lock": xblock.fields['visible_to_staff_only'].is_set_on(xblock),
        "start": xblock.fields['start'].to_json(xblock.start),
        "graded": xblock.graded,
        "due_date": get_default_time_display(xblock.due),
        "due": xblock.fields['due'].to_json(xblock.due),
        "format": xblock.format,
        "course_graders": json.dumps([grader.get('type') for grader in graders]),
        "has_changes": has_changes,
        "actions": xblock_actions,
        "explanatory_message": explanatory_message
    }

    # Entrance exam subsection should be hidden. in_entrance_exam is inherited metadata, all children will have it.
    if xblock.category == 'sequential' and getattr(xblock, "in_entrance_exam", False):
        xblock_info["is_header_visible"] = False

    if data is not None:
        xblock_info["data"] = data
    if metadata is not None:
        xblock_info["metadata"] = metadata
    if include_ancestor_info:
        xblock_info['ancestor_info'] = _create_xblock_ancestor_info(xblock, course_outline)
    if child_info:
        xblock_info['child_info'] = child_info
    if visibility_state == VisibilityState.staff_only:
        xblock_info["ancestor_has_staff_lock"] = ancestor_has_staff_lock(xblock, parent_xblock)
    else:
        xblock_info["ancestor_has_staff_lock"] = False

    if course_outline:
        if xblock_info["has_explicit_staff_lock"]:
            xblock_info["staff_only_message"] = True
        elif child_info and child_info["children"]:
            xblock_info["staff_only_message"] = all([child["staff_only_message"] for child in child_info["children"]])
        else:
            xblock_info["staff_only_message"] = False

    return xblock_info
Beispiel #25
0
def subsection_handler(request, tag=None, package_id=None, branch=None, version_guid=None, block=None):
    """
    The restful handler for subsection-specific requests.

    GET
        html: return html page for editing a subsection
        json: not currently supported
    """
    if 'text/html' in request.META.get('HTTP_ACCEPT', 'text/html'):
        locator = BlockUsageLocator(package_id=package_id, branch=branch, version_guid=version_guid, block_id=block)
        try:
            old_location, course, item, lms_link = _get_item_in_course(request, locator)
        except ItemNotFoundError:
            return HttpResponseBadRequest()

        preview_link = get_lms_link_for_item(old_location, course_id=course.location.course_id, preview=True)

        # make sure that location references a 'sequential', otherwise return
        # BadRequest
        if item.location.category != 'sequential':
            return HttpResponseBadRequest()

        parent = get_parent_xblock(item)

        # remove all metadata from the generic dictionary that is presented in a
        # more normalized UI. We only want to display the XBlocks fields, not
        # the fields from any mixins that have been added
        fields = getattr(item, 'unmixed_class', item.__class__).fields

        policy_metadata = dict(
            (field.name, field.read_from(item))
            for field
            in fields.values()
            if field.name not in ['display_name', 'start', 'due', 'format'] and field.scope == Scope.settings
        )

        can_view_live = False
        subsection_units = item.get_children()
        for unit in subsection_units:
            state = compute_publish_state(unit)
            if state in (PublishState.public, PublishState.draft):
                can_view_live = True
                break

        course_locator = loc_mapper().translate_location(
            course.location.course_id, course.location, False, True
        )

        return render_to_response(
            'edit_subsection.html',
            {
                'subsection': item,
                'context_course': course,
                'new_unit_category': 'vertical',
                'lms_link': lms_link,
                'preview_link': preview_link,
                'course_graders': json.dumps(CourseGradingModel.fetch(course_locator).graders),
                'parent_item': parent,
                'locator': locator,
                'policy_metadata': policy_metadata,
                'subsection_units': subsection_units,
                'can_view_live': can_view_live
            }
        )
    else:
        return HttpResponseBadRequest("Only supports html requests")