Beispiel #1
0
def import_handler(request, course_key_string):
    """
    The restful handler for importing a course.

    GET
        html: return html page for import page
        json: not supported
    POST or PUT
        json: import a course via the .tar.gz file specified in request.FILES
    """
    courselike_key = CourseKey.from_string(course_key_string)
    library = isinstance(courselike_key, LibraryLocator)
    if library:
        root_name = LIBRARY_ROOT
        successful_url = reverse_library_url('library_handler', courselike_key)
        context_name = 'context_library'
        courselike_module = modulestore().get_library(courselike_key)
        import_func = import_library_from_xml
    else:
        root_name = COURSE_ROOT
        successful_url = reverse_course_url('course_handler', courselike_key)
        context_name = 'context_course'
        courselike_module = modulestore().get_course(courselike_key)
        import_func = import_course_from_xml
    return _import_handler(request, courselike_key, root_name, successful_url,
                           context_name, courselike_module, import_func)
Beispiel #2
0
    def test_manage_library_users(self):
        """
        Simple test that the Library "User Access" view works.
        Also tests that we can use the REST API to assign a user to a library.
        """
        library = LibraryFactory.create()
        extra_user, _ = self.create_non_staff_user()
        manage_users_url = reverse_library_url('manage_library_users', unicode(library.location.library_key))

        response = self.client.get(manage_users_url)
        self.assertEqual(response.status_code, 200)
        # extra_user has not been assigned to the library so should not show up in the list:
        self.assertNotIn(extra_user.username, response.content)

        # Now add extra_user to the library:
        user_details_url = reverse_course_url(
            'course_team_handler',
            library.location.library_key, kwargs={'email': extra_user.email}
        )
        edit_response = self.client.ajax_post(user_details_url, {"role": LibraryUserRole.ROLE})
        self.assertIn(edit_response.status_code, (200, 204))

        # Now extra_user should apear in the list:
        response = self.client.get(manage_users_url)
        self.assertEqual(response.status_code, 200)
        self.assertIn(extra_user.username, response.content)
Beispiel #3
0
def manage_library_users(request, library_key_string):
    """
    Studio UI for editing the users within a library.

    Uses the /course_team/:library_key/:user_email/ REST API to make changes.
    """
    library_key = CourseKey.from_string(library_key_string)
    if not isinstance(library_key, LibraryLocator):
        raise Http404  # This is not a library
    user_perms = get_user_permissions(request.user, library_key)
    if not user_perms & STUDIO_VIEW_USERS:
        raise PermissionDenied()
    library = modulestore().get_library(library_key)
    if library is None:
        raise Http404

    # Segment all the users explicitly associated with this library, ensuring each user only has one role listed:
    instructors = set(CourseInstructorRole(library_key).users_with_role())
    staff = set(CourseStaffRole(library_key).users_with_role()) - instructors
    users = set(LibraryUserRole(library_key).users_with_role()) - instructors - staff
    all_users = instructors | staff | users

    return render_to_response('manage_users_lib.html', {
        'context_library': library,
        'staff': staff,
        'instructors': instructors,
        'users': users,
        'all_users': all_users,
        'allow_actions': bool(user_perms & STUDIO_EDIT_ROLES),
        'library_key': unicode(library_key),
        'lib_users_url': reverse_library_url('manage_library_users', library_key_string),
    })
Beispiel #4
0
def import_handler(request, course_key_string):
    """
    The restful handler for importing a course.

    GET
        html: return html page for import page
        json: not supported
    POST or PUT
        json: import a course via the .tar.gz file specified in request.FILES
    """
    courselike_key = CourseKey.from_string(course_key_string)
    library = isinstance(courselike_key, LibraryLocator)
    if library:
        root_name = LIBRARY_ROOT
        successful_url = reverse_library_url('library_handler', courselike_key)
        context_name = 'context_library'
        courselike_module = modulestore().get_library(courselike_key)
        import_func = import_library_from_xml
    else:
        root_name = COURSE_ROOT
        successful_url = reverse_course_url('course_handler', courselike_key)
        context_name = 'context_course'
        courselike_module = modulestore().get_course(courselike_key)
        import_func = import_course_from_xml
    return _import_handler(
        request, courselike_key, root_name, successful_url, context_name, courselike_module, import_func
    )
def export(request, course_key_string):
    """
    Trigger the async export job
    https://github.com/edx/edx-platform/blob/open-release/juniper.master/cms/djangoapps/contentstore/views/import_export.py#L290

    POST /sn-api/courses/<course_key>/export/
    """
    course_key = CourseKey.from_string(course_key_string)
    if isinstance(course_key, LibraryLocator):
        courselike_module = modulestore().get_library(course_key)
        context = {
            'context_library':
            courselike_module,
            'courselike_home_url':
            reverse_library_url("library_handler", course_key),
            'library':
            True
        }
    else:
        courselike_module = modulestore().get_course(course_key)
        if courselike_module is None:
            raise Http404
        context = {
            'context_course': courselike_module,
            'courselike_home_url': reverse_course_url("course_handler",
                                                      course_key),
            'library': False
        }
    context['status_url'] = reverse_course_url('export_status_handler',
                                               course_key)

    export_olx.delay(request.user.id, course_key_string, request.LANGUAGE_CODE)
    return Response({'ExportStatus': 1})
Beispiel #6
0
    def test_manage_library_users(self):
        """
        Simple test that the Library "User Access" view works.
        Also tests that we can use the REST API to assign a user to a library.
        """
        library = LibraryFactory.create()
        extra_user, _ = self.create_non_staff_user()
        manage_users_url = reverse_library_url(
            'manage_library_users', text_type(library.location.library_key))

        response = self.client.get(manage_users_url)
        self.assertEqual(response.status_code, 200)
        # extra_user has not been assigned to the library so should not show up in the list:
        self.assertNotIn(binary_type(extra_user.username), response.content)

        # Now add extra_user to the library:
        user_details_url = reverse_course_url(
            'course_team_handler',
            library.location.library_key,
            kwargs={'email': extra_user.email})
        edit_response = self.client.ajax_post(user_details_url,
                                              {"role": LibraryUserRole.ROLE})
        self.assertIn(edit_response.status_code, (200, 204))

        # Now extra_user should apear in the list:
        response = self.client.get(manage_users_url)
        self.assertEqual(response.status_code, 200)
        self.assertIn(binary_type(extra_user.username), response.content)
Beispiel #7
0
def export_handler(request, course_key_string):
    """
    The restful handler for exporting a course.

    GET
        html: return html page for import page
        application/x-tgz: return tar.gz file containing exported course
        json: not supported

    Note that there are 2 ways to request the tar.gz file. The request header can specify
    application/x-tgz via HTTP_ACCEPT, or a query parameter can be used (?_accept=application/x-tgz).

    If the tar.gz file has been requested but the export operation fails, an HTML page will be returned
    which describes the error.
    """
    course_key = CourseKey.from_string(course_key_string)
    export_url = reverse_course_url('export_handler', course_key)
    if not has_course_author_access(request.user, course_key):
        raise PermissionDenied()

    if isinstance(course_key, LibraryLocator):
        courselike_module = modulestore().get_library(course_key)
        context = {
            'context_library':
            courselike_module,
            'courselike_home_url':
            reverse_library_url("library_handler", course_key),
            'library':
            True
        }
    else:
        courselike_module = modulestore().get_course(course_key)
        context = {
            'context_course': courselike_module,
            'courselike_home_url': reverse_course_url("course_handler",
                                                      course_key),
            'library': False
        }

    context['export_url'] = export_url + '?_accept=application/x-tgz'

    # an _accept URL parameter will be preferred over HTTP_ACCEPT in the header.
    requested_format = request.REQUEST.get(
        '_accept', request.META.get('HTTP_ACCEPT', 'text/html'))

    if 'application/x-tgz' in requested_format:
        try:
            tarball = create_export_tarball(courselike_module, course_key,
                                            context)
        except SerializationError:
            return render_to_response('export.html', context)
        return send_tarball(tarball)

    elif 'text/html' in requested_format:
        return render_to_response('export.html', context)

    else:
        # Only HTML or x-tgz request formats are supported (no JSON).
        return HttpResponse(status=406)
Beispiel #8
0
def _create_library(request):
    """
    Helper method for creating a new library.
    """
    if not auth.has_access(request.user, CourseCreatorRole()):
        log.exception(u"User %s tried to create a library without permission",
                      request.user.username)
        raise PermissionDenied()
    display_name = None
    try:
        display_name = request.json['display_name']
        org = request.json['org']
        library = request.json.get('number', None)
        if library is None:
            library = request.json['library']
        store = modulestore()
        with store.default_store(ModuleStoreEnum.Type.split):
            new_lib = store.create_library(
                org=org,
                library=library,
                user_id=request.user.id,
                fields={"display_name": display_name},
            )
        # Give the user admin ("Instructor") role for this library:
        add_instructor(new_lib.location.library_key, request.user,
                       request.user)
    except KeyError as error:
        log.exception("Unable to create library - missing required JSON key.")
        return JsonResponseBadRequest({
            "ErrMsg":
            _("Unable to create library - missing required field '{field}'".
              format(field=error.message))
        })
    except InvalidKeyError as error:
        log.exception("Unable to create library - invalid key.")
        return JsonResponseBadRequest({
            "ErrMsg":
            _("Unable to create library '{name}'.\n\n{err}").format(
                name=display_name, err=error.message)
        })
    except DuplicateCourseError:
        log.exception(
            "Unable to create library - one already exists with the same key.")
        return JsonResponseBadRequest({
            'ErrMsg':
            _('There is already a library defined with the same '
              'organization and library code. Please '
              'change your library code so that it is unique within your organization.'
              )
        })

    lib_key_str = unicode(new_lib.location.library_key)
    return JsonResponse({
        'url':
        reverse_library_url('library_handler', lib_key_str),
        'library_key':
        lib_key_str,
    })
Beispiel #9
0
def _create_library(request):
    """
    Helper method for creating a new library.
    """
    display_name = None
    try:
        display_name = request.json['display_name']
        org = request.json['org']
        library = request.json.get('number', None)

        # [COLARAZ_CUSTOM]
        if not request.user.is_superuser and not org in configuration_helpers.get_value('course_org_filter'):
            log.exception('Unable to create library - invalid organization ({}).'.format(org))
            return JsonResponseBadRequest({
                "ErrMsg": _("Unable to create library '{}'.\n\n{} is not a valid organization.").format(display_name, org)
            })


        if library is None:
            library = request.json['library']
        store = modulestore()
        with store.default_store(ModuleStoreEnum.Type.split):
            new_lib = store.create_library(
                org=org,
                library=library,
                user_id=request.user.id,
                fields={"display_name": display_name},
            )
        # Give the user admin ("Instructor") role for this library:
        add_instructor(new_lib.location.library_key, request.user, request.user)
    except KeyError as error:
        log.exception("Unable to create library - missing required JSON key.")
        return JsonResponseBadRequest({
            "ErrMsg": _("Unable to create library - missing required field '{field}'").format(field=text_type(error))
        })
    except InvalidKeyError as error:
        log.exception("Unable to create library - invalid key.")
        return JsonResponseBadRequest({
            "ErrMsg": _("Unable to create library '{name}'.\n\n{err}").format(name=display_name, err=text_type(error))
        })
    except DuplicateCourseError:
        log.exception("Unable to create library - one already exists with the same key.")
        return JsonResponseBadRequest({
            'ErrMsg': _(
                'There is already a library defined with the same '
                'organization and library code. Please '
                'change your library code so that it is unique within your organization.'
            )
        })

    lib_key_str = unicode(new_lib.location.library_key)
    return JsonResponse({
        'url': reverse_library_url('library_handler', lib_key_str),
        'library_key': lib_key_str,
    })
Beispiel #10
0
def export_handler(request, course_key_string):
    """
    The restful handler for exporting a course.

    GET
        html: return html page for import page
        application/x-tgz: return tar.gz file containing exported course
        json: not supported

    Note that there are 2 ways to request the tar.gz file. The request header can specify
    application/x-tgz via HTTP_ACCEPT, or a query parameter can be used (?_accept=application/x-tgz).

    If the tar.gz file has been requested but the export operation fails, an HTML page will be returned
    which describes the error.
    """
    course_key = CourseKey.from_string(course_key_string)
    export_url = reverse_course_url('export_handler', course_key)
    if not has_course_author_access(request.user, course_key):
        raise PermissionDenied()

    if isinstance(course_key, LibraryLocator):
        courselike_module = modulestore().get_library(course_key)
        context = {
            'context_library': courselike_module,
            'courselike_home_url': reverse_library_url("library_handler", course_key),
            'library': True
        }
    else:
        courselike_module = modulestore().get_course(course_key)
        if courselike_module is None:
            raise Http404
        context = {
            'context_course': courselike_module,
            'courselike_home_url': reverse_course_url("course_handler", course_key),
            'library': False
        }

    context['export_url'] = export_url + '?_accept=application/x-tgz'

    # an _accept URL parameter will be preferred over HTTP_ACCEPT in the header.
    requested_format = request.GET.get('_accept', request.META.get('HTTP_ACCEPT', 'text/html'))

    if 'application/x-tgz' in requested_format:
        try:
            tarball = create_export_tarball(courselike_module, course_key, context)
        except SerializationError:
            return render_to_response('export.html', context)
        return send_tarball(tarball)

    elif 'text/html' in requested_format:
        return render_to_response('export.html', context)

    else:
        # Only HTML or x-tgz request formats are supported (no JSON).
        return HttpResponse(status=406)
Beispiel #11
0
def library_blocks_view(library, user, response_format):
    """
    The main view of a course's content library.
    Shows all the XBlocks in the library, and allows adding/editing/deleting
    them.
    Can be called with response_format="json" to get a JSON-formatted list of
    the XBlocks in the library along with library metadata.

    Assumes that read permissions have been checked before calling this.
    """
    assert isinstance(library.location.library_key, LibraryLocator)
    assert isinstance(library.location, LibraryUsageLocator)

    children = library.children
    if response_format == "json":
        # The JSON response for this request is short and sweet:
        prev_version = library.runtime.course_entry.structure[
            'previous_version']
        return JsonResponse({
            "display_name":
            library.display_name,
            "library_id":
            unicode(library.location.library_key),
            "version":
            unicode(library.runtime.course_entry.course_key.version),
            "previous_version":
            unicode(prev_version) if prev_version else None,
            "blocks": [unicode(x) for x in children],
        })

    can_edit = has_studio_write_access(user, library.location.library_key)

    xblock_info = create_xblock_info(library,
                                     include_ancestor_info=False,
                                     graders=[])
    component_templates = get_component_templates(
        library, library=True) if can_edit else []

    return render_to_response(
        'library.html', {
            'can_edit':
            can_edit,
            'context_library':
            library,
            'component_templates':
            json.dumps(component_templates),
            'xblock_info':
            xblock_info,
            'templates':
            CONTAINER_TEMPATES,
            'lib_users_url':
            reverse_library_url('manage_library_users',
                                unicode(library.location.library_key)),
        })
Beispiel #12
0
def export_handler(request, course_key_string):
    """
    The restful handler for exporting a course.

    GET
        html: return html page for import page
        json: not supported
    POST
        Start a Celery task to export the course

    The Studio UI uses a POST request to start the export asynchronously, with
    a link appearing on the page once it's ready.
    """
    course_key = CourseKey.from_string(course_key_string)
    if not has_course_author_access(request.user, course_key):
        raise PermissionDenied()

    if isinstance(course_key, LibraryLocator):
        courselike_module = modulestore().get_library(course_key)
        context = {
            'context_library':
            courselike_module,
            'courselike_home_url':
            reverse_library_url("library_handler", course_key),
            'library':
            True
        }
    else:
        courselike_module = modulestore().get_course(course_key)
        if courselike_module is None:
            raise Http404
        context = {
            'context_course': courselike_module,
            'courselike_home_url': reverse_course_url("course_handler",
                                                      course_key),
            'library': False
        }
    context['status_url'] = reverse_course_url('export_status_handler',
                                               course_key)

    # an _accept URL parameter will be preferred over HTTP_ACCEPT in the header.
    requested_format = request.GET.get(
        '_accept', request.META.get('HTTP_ACCEPT', 'text/html'))

    if request.method == 'POST':
        export_olx.delay(request.user.id, course_key_string,
                         request.LANGUAGE_CODE)
        return JsonResponse({'ExportStatus': 1})
    elif 'text/html' in requested_format:
        return render_to_response('export.html', context)
    else:
        # Only HTML request format is supported (no JSON).
        return HttpResponse(status=406)
Beispiel #13
0
    def _can_access_library(self, library):
        """
        Use the normal studio library URL to check if we have access

        `library` can be a LibraryLocator or the library's root XBlock
        """
        if isinstance(library, (basestring, LibraryLocator)):
            lib_key = library
        else:
            lib_key = library.location.library_key
        response = self.client.get(reverse_library_url("library_handler", unicode(lib_key)))
        self.assertIn(response.status_code, (200, 302, 403))
        return response.status_code == 200
    def _can_access_library(self, library):
        """
        Use the normal studio library URL to check if we have access

        `library` can be a LibraryLocator or the library's root XBlock
        """
        if isinstance(library, (six.string_types, LibraryLocator)):
            lib_key = library
        else:
            lib_key = library.location.library_key
        response = self.client.get(reverse_library_url('library_handler', six.text_type(lib_key)))
        self.assertIn(response.status_code, (200, 302, 403))
        return response.status_code == 200
Beispiel #15
0
def _create_library(request):
    """
    Helper method for creating a new library.
    """
    if not auth.has_access(request.user, CourseCreatorRole()):
        log.exception(u"User %s tried to create a library without permission", request.user.username)
        raise PermissionDenied()
    display_name = None
    try:
        display_name = request.json['display_name']
        org = request.json['org']
        library = request.json.get('number', None)
        if library is None:
            library = request.json['library']
        store = modulestore()
        with store.default_store(ModuleStoreEnum.Type.split):
            new_lib = store.create_library(
                org=org,
                library=library,
                user_id=request.user.id,
                fields={"display_name": display_name},
            )
        # Give the user admin ("Instructor") role for this library:
        add_instructor(new_lib.location.library_key, request.user, request.user)
    except KeyError as error:
        log.exception("Unable to create library - missing required JSON key.")
        return JsonResponseBadRequest({
            "ErrMsg": _("Unable to create library - missing required field '{field}'".format(field=error.message))
        })
    except InvalidKeyError as error:
        log.exception("Unable to create library - invalid key.")
        return JsonResponseBadRequest({
            "ErrMsg": _("Unable to create library '{name}'.\n\n{err}").format(name=display_name, err=error.message)
        })
    except DuplicateCourseError:
        log.exception("Unable to create library - one already exists with the same key.")
        return JsonResponseBadRequest({
            'ErrMsg': _(
                'There is already a library defined with the same '
                'organization and library code. Please '
                'change your library code so that it is unique within your organization.'
            )
        })

    lib_key_str = unicode(new_lib.location.library_key)
    return JsonResponse({
        'url': reverse_library_url('library_handler', lib_key_str),
        'library_key': lib_key_str,
    })
 def _assert_library_link_present(response, library):
     """
     Asserts there's a valid library link on libraries tab.
     """
     parsed_html = lxml.html.fromstring(response.content)
     library_link_elements = parsed_html.find_class('library-link')
     self.assertEqual(len(library_link_elements), 1)
     link = library_link_elements[0]
     self.assertEqual(
         link.get("href"),
         reverse_library_url('library_handler', library.location.library_key),
     )
     # now test that url
     outline_response = self.client.get(link.get("href"), {}, HTTP_ACCEPT='text/html')
     self.assertEqual(outline_response.status_code, 200)
Beispiel #17
0
 def _assert_library_link_present(response, library):
     """
     Asserts there's a valid library link on libraries tab.
     """
     parsed_html = lxml.html.fromstring(response.content)
     library_link_elements = parsed_html.find_class('library-link')
     self.assertEqual(len(library_link_elements), 1)
     link = library_link_elements[0]
     self.assertEqual(
         link.get("href"),
         reverse_library_url('library_handler', library.location.library_key),
     )
     # now test that url
     outline_response = self.client.get(link.get("href"), {}, HTTP_ACCEPT='text/html')
     self.assertEqual(outline_response.status_code, 200)
Beispiel #18
0
def export_handler(request, course_key_string):
    """
    The restful handler for exporting a course.

    GET
        html: return html page for import page
        json: not supported
    POST
        Start a Celery task to export the course

    The Studio UI uses a POST request to start the export asynchronously, with
    a link appearing on the page once it's ready.
    """
    course_key = CourseKey.from_string(course_key_string)
    if not has_course_author_access(request.user, course_key):
        raise PermissionDenied()

    if isinstance(course_key, LibraryLocator):
        courselike_module = modulestore().get_library(course_key)
        context = {
            'context_library': courselike_module,
            'courselike_home_url': reverse_library_url("library_handler", course_key),
            'library': True
        }
    else:
        courselike_module = modulestore().get_course(course_key)
        if courselike_module is None:
            raise Http404
        context = {
            'context_course': courselike_module,
            'courselike_home_url': reverse_course_url("course_handler", course_key),
            'library': False
        }
    context['status_url'] = reverse_course_url('export_status_handler', course_key)

    # an _accept URL parameter will be preferred over HTTP_ACCEPT in the header.
    requested_format = request.GET.get('_accept', request.META.get('HTTP_ACCEPT', 'text/html'))

    if request.method == 'POST':
        export_olx.delay(request.user.id, course_key_string, request.LANGUAGE_CODE)
        return JsonResponse({'ExportStatus': 1})
    elif 'text/html' in requested_format:
        return render_to_response('export.html', context)
    else:
        # Only HTML request format is supported (no JSON).
        return HttpResponse(status=406)
Beispiel #19
0
def format_library_for_view(library, this_user=None):
    """
    Return a dict of the data which the view requires for each library
    """
    if this_user:
        user = this_user
    else:
        user = request.user

    return {
        'display_name': library.display_name,
        'library_key': unicode(library.location.library_key),
        'url': reverse_library_url('library_handler', unicode(library.location.library_key)),
        'org': library.display_org_with_default,
        'number': library.display_number_with_default,
        'can_edit': has_studio_write_access(user, library.location.library_key),
    }
    def test_libraries_on_course_index(self):
        """
        Test getting the list of libraries from the course listing page
        """
        # Add a library:
        lib1 = LibraryFactory.create()

        index_url = "/home/"
        index_response = self.client.get(index_url, {}, HTTP_ACCEPT="text/html")
        parsed_html = lxml.html.fromstring(index_response.content)
        library_link_elements = parsed_html.find_class("library-link")
        self.assertEqual(len(library_link_elements), 1)
        link = library_link_elements[0]
        self.assertEqual(link.get("href"), reverse_library_url("library_handler", lib1.location.library_key))
        # now test that url
        outline_response = self.client.get(link.get("href"), {}, HTTP_ACCEPT="text/html")
        self.assertEqual(outline_response.status_code, 200)
Beispiel #21
0
def manage_library_users(request, library_key_string):
    """
    Studio UI for editing the users within a library.

    Uses the /course_team/:library_key/:user_email/ REST API to make changes.
    """
    library_key = CourseKey.from_string(library_key_string)
    if not isinstance(library_key, LibraryLocator):
        raise Http404  # This is not a library
    user_perms = get_user_permissions(request.user, library_key)
    if not user_perms & STUDIO_VIEW_USERS:
        raise PermissionDenied()
    library = modulestore().get_library(library_key)
    if library is None:
        raise Http404

    # Segment all the users explicitly associated with this library, ensuring each user only has one role listed:
    instructors = set(CourseInstructorRole(library_key).users_with_role())
    staff = set(CourseStaffRole(library_key).users_with_role()) - instructors
    users = set(
        LibraryUserRole(library_key).users_with_role()) - instructors - staff

    formatted_users = []
    for user in instructors:
        formatted_users.append(user_with_role(user, 'instructor'))
    for user in staff:
        formatted_users.append(user_with_role(user, 'staff'))
    for user in users:
        formatted_users.append(user_with_role(user, 'library_user'))

    return render_to_response(
        'manage_users_lib.html', {
            'context_library':
            library,
            'users':
            formatted_users,
            'allow_actions':
            bool(user_perms & STUDIO_EDIT_ROLES),
            'library_key':
            unicode(library_key),
            'lib_users_url':
            reverse_library_url('manage_library_users', library_key_string),
            'show_children_previews':
            library.show_children_previews
        })
Beispiel #22
0
def xblock_studio_url(xblock, parent_xblock=None):
    """
    Returns the Studio editing URL for the specified xblock.
    """
    if not xblock_has_own_studio_page(xblock, parent_xblock):
        return None
    category = xblock.category
    if category == 'course':
        return reverse_course_url('course_handler', xblock.location.course_key)
    elif category in ('chapter', 'sequential'):
        return u'{url}?show={usage_key}'.format(
            url=reverse_course_url('course_handler', xblock.location.course_key),
            usage_key=urllib.quote(unicode(xblock.location))
        )
    elif category == 'library':
        library_key = xblock.location.course_key
        return reverse_library_url('library_handler', library_key)
    else:
        return reverse_usage_url('container_handler', xblock.location)
Beispiel #23
0
def xblock_studio_url(xblock, parent_xblock=None):
    """
    Returns the Studio editing URL for the specified xblock.
    """
    if not xblock_has_own_studio_page(xblock, parent_xblock):
        return None
    category = xblock.category
    if category == "course":
        return reverse_course_url("course_handler", xblock.location.course_key)
    elif category in ("chapter", "sequential"):
        return u"{url}?show={usage_key}".format(
            url=reverse_course_url("course_handler", xblock.location.course_key),
            usage_key=urllib.quote(unicode(xblock.location)),
        )
    elif category == "library":
        library_key = xblock.location.course_key
        return reverse_library_url("library_handler", library_key)
    else:
        return reverse_usage_url("container_handler", xblock.location)
Beispiel #24
0
def xblock_studio_url(xblock, parent_xblock=None):
    """
    Returns the Studio editing URL for the specified xblock.
    """
    if not xblock_has_own_studio_page(xblock, parent_xblock):
        return None
    category = xblock.category
    if category == 'course':
        return reverse_course_url('course_handler', xblock.location.course_key)
    elif category in ('chapter', 'sequential'):
        return u'{url}?show={usage_key}'.format(
            url=reverse_course_url('course_handler', xblock.location.course_key),
            usage_key=urllib.quote(unicode(xblock.location))
        )
    elif category == 'library':
        library_key = xblock.location.course_key
        return reverse_library_url('library_handler', library_key)
    else:
        return reverse_usage_url('container_handler', xblock.location)
Beispiel #25
0
def import_handler(request, course_key_string):
    """
    The restful handler for importing a course.

    GET
        html: return html page for import page
        json: not supported
    POST or PUT
        json: import a course via the .tar.gz file specified in request.FILES
    """
    courselike_key = CourseKey.from_string(course_key_string)
    library = isinstance(courselike_key, LibraryLocator)
    if library:
        successful_url = reverse_library_url('library_handler', courselike_key)
        context_name = 'context_library'
        courselike_module = modulestore().get_library(courselike_key)
    else:
        successful_url = reverse_course_url('course_handler', courselike_key)
        context_name = 'context_course'
        courselike_module = modulestore().get_course(courselike_key)
    if not has_course_author_access(request.user, courselike_key):
        raise PermissionDenied()

    if 'application/json' in request.META.get('HTTP_ACCEPT',
                                              'application/json'):
        if request.method == 'GET':
            raise NotImplementedError('coming soon')
        else:
            return _write_chunk(request, courselike_key)
    elif request.method == 'GET':  # assume html
        status_url = reverse_course_url("import_status_handler",
                                        courselike_key,
                                        kwargs={'filename': "fillerName"})
        return render_to_response(
            'import.html', {
                context_name: courselike_module,
                'successful_import_redirect_url': successful_url,
                'import_status_url': status_url,
                'library': isinstance(courselike_key, LibraryLocator)
            })
    else:
        return HttpResponseNotFound()
def import_handler(request, course_key_string):
    """
    The restful handler for the import page.

    GET
        html: return html page for import page
    """
    courselike_key = CourseKey.from_string(course_key_string)
    library = isinstance(courselike_key, LibraryLocator)
    if library:
        successful_url = reverse_library_url("library_handler", courselike_key)
        courselike_module = modulestore().get_library(courselike_key)
        context_name = "context_library"
    else:
        successful_url = reverse_course_url("course_handler", courselike_key)
        courselike_module = modulestore().get_course(courselike_key)
        context_name = "context_course"

    if not has_course_author_access(request.user, courselike_key):
        raise PermissionDenied()

    return render_to_response(
        "import.html", {
            context_name:
            courselike_module,
            "successful_import_redirect_url":
            successful_url,
            "import_status_url":
            reverse("course_import_status_handler",
                    kwargs={
                        "course_key_string": unicode(courselike_key),
                        "filename": "fillerName"
                    }),
            "import_url":
            reverse("course_import_export_handler",
                    kwargs={
                        "course_key_string": unicode(courselike_key),
                    }),
            "library":
            library
        })
Beispiel #27
0
def import_handler(request, course_key_string):
    """
    The restful handler for importing a course.

    GET
        html: return html page for import page
        json: not supported
    POST or PUT
        json: import a course via the .tar.gz file specified in request.FILES
    """
    courselike_key = CourseKey.from_string(course_key_string)
    library = isinstance(courselike_key, LibraryLocator)
    if library:
        successful_url = reverse_library_url('library_handler', courselike_key)
        context_name = 'context_library'
        courselike_module = modulestore().get_library(courselike_key)
    else:
        successful_url = reverse_course_url('course_handler', courselike_key)
        context_name = 'context_course'
        courselike_module = modulestore().get_course(courselike_key)
    if not has_course_author_access(request.user, courselike_key):
        raise PermissionDenied()

    if 'application/json' in request.META.get('HTTP_ACCEPT', 'application/json'):
        if request.method == 'GET':
            raise NotImplementedError('coming soon')
        else:
            return _write_chunk(request, courselike_key)
    elif request.method == 'GET':  # assume html
        status_url = reverse_course_url(
            "import_status_handler", courselike_key, kwargs={'filename': "fillerName"}
        )
        return render_to_response('import.html', {
            context_name: courselike_module,
            'successful_import_redirect_url': successful_url,
            'import_status_url': status_url,
            'library': isinstance(courselike_key, LibraryLocator)
        })
    else:
        return HttpResponseNotFound()
Beispiel #28
0
def import_handler(request, course_key_string):
    """
    The restful handler for the import page.

    GET
        html: return html page for import page
    """
    courselike_key = CourseKey.from_string(course_key_string)
    library = isinstance(courselike_key, LibraryLocator)
    if library:
        successful_url = reverse_library_url("library_handler", courselike_key)
        courselike_module = modulestore().get_library(courselike_key)
        context_name = "context_library"
    else:
        successful_url = reverse_course_url("course_handler", courselike_key)
        courselike_module = modulestore().get_course(courselike_key)
        context_name = "context_course"

    if not has_course_author_access(request.user, courselike_key):
        raise PermissionDenied()

    return render_to_response("import.html", {
        context_name: courselike_module,
        "successful_import_redirect_url": successful_url,
        "import_status_url": reverse(
            "course_import_status_handler",
            kwargs={
                "course_key_string": unicode(courselike_key),
                "filename": "fillerName"
            }
        ),
        "import_url": reverse(
            "course_import_export_handler",
            kwargs={
                "course_key_string": unicode(courselike_key),
            }
        ),
        "library": library
    })
Beispiel #29
0
    def test_libraries_on_course_index(self):
        """
        Test getting the list of libraries from the course listing page
        """
        # Add a library:
        lib1 = LibraryFactory.create()

        index_url = '/home/'
        index_response = self.client.get(index_url, {},
                                         HTTP_ACCEPT='text/html')
        parsed_html = lxml.html.fromstring(index_response.content)
        library_link_elements = parsed_html.find_class('library-link')
        self.assertEqual(len(library_link_elements), 1)
        link = library_link_elements[0]
        self.assertEqual(
            link.get("href"),
            reverse_library_url('library_handler', lib1.location.library_key),
        )
        # now test that url
        outline_response = self.client.get(link.get("href"), {},
                                           HTTP_ACCEPT='text/html')
        self.assertEqual(outline_response.status_code, 200)
Beispiel #30
0
def library_blocks_view(library, user, response_format):
    """
    The main view of a course's content library.
    Shows all the XBlocks in the library, and allows adding/editing/deleting
    them.
    Can be called with response_format="json" to get a JSON-formatted list of
    the XBlocks in the library along with library metadata.

    Assumes that read permissions have been checked before calling this.
    """
    assert isinstance(library.location.library_key, LibraryLocator)
    assert isinstance(library.location, LibraryUsageLocator)

    children = library.children
    if response_format == "json":
        # The JSON response for this request is short and sweet:
        prev_version = library.runtime.course_entry.structure['previous_version']
        return JsonResponse({
            "display_name": library.display_name,
            "library_id": unicode(library.location.library_key),
            "version": unicode(library.runtime.course_entry.course_key.version),
            "previous_version": unicode(prev_version) if prev_version else None,
            "blocks": [unicode(x) for x in children],
        })

    can_edit = has_studio_write_access(user, library.location.library_key)

    xblock_info = create_xblock_info(library, include_ancestor_info=False, graders=[])
    component_templates = get_component_templates(library, library=True) if can_edit else []

    return render_to_response('library.html', {
        'can_edit': can_edit,
        'context_library': library,
        'component_templates': json.dumps(component_templates),
        'xblock_info': xblock_info,
        'templates': CONTAINER_TEMPATES,
        'lib_users_url': reverse_library_url('manage_library_users', unicode(library.location.library_key)),
    })
Beispiel #31
0
def export_handler(request, course_key_string):
    """
    The restful handler for exporting a course.

    GET
        html: return html page for import page
        application/x-tgz: return tar.gz file containing exported course
        json: not supported
    POST
        Start a Celery task to export the course

    Note that there are 3 ways to request the tar.gz file.  The Studio UI uses
    a POST request to start the export asynchronously, with a link appearing
    on the page once it's ready.  Additionally, for backwards compatibility
    reasons the request header can specify application/x-tgz via HTTP_ACCEPT,
    or a query parameter can be used (?_accept=application/x-tgz); this will
    export the course synchronously and return the resulting file (unless the
    request times out for a large course).

    If the tar.gz file has been requested but the export operation fails, the
    import page will be returned including a description of the error.
    """
    course_key = CourseKey.from_string(course_key_string)
    if not has_course_author_access(request.user, course_key):
        raise PermissionDenied()

    if isinstance(course_key, LibraryLocator):
        courselike_module = modulestore().get_library(course_key)
        context = {
            'context_library': courselike_module,
            'courselike_home_url': reverse_library_url("library_handler", course_key),
            'library': True
        }
    else:
        courselike_module = modulestore().get_course(course_key)
        if courselike_module is None:
            raise Http404
        context = {
            'context_course': courselike_module,
            'courselike_home_url': reverse_course_url("course_handler", course_key),
            'library': False
        }
    context['status_url'] = reverse_course_url('export_status_handler', course_key)

    # an _accept URL parameter will be preferred over HTTP_ACCEPT in the header.
    requested_format = request.GET.get('_accept', request.META.get('HTTP_ACCEPT', 'text/html'))

    if request.method == 'POST':
        export_olx.delay(request.user.id, course_key_string, request.LANGUAGE_CODE)
        return JsonResponse({'ExportStatus': 1})
    elif 'application/x-tgz' in requested_format:
        try:
            tarball = create_export_tarball(courselike_module, course_key, context)
            return send_tarball(tarball)
        except SerializationError:
            return render_to_response('export.html', context)
    elif 'text/html' in requested_format:
        return render_to_response('export.html', context)
    else:
        # Only HTML or x-tgz request formats are supported (no JSON).
        return HttpResponse(status=406)
Beispiel #32
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 "lib-block-v1" in usage_key_string:

        try:
            usage_key = UsageKey.from_string(usage_key_string)
        except InvalidKeyError:
            raise Http404

        try:
            library_key_string = re.sub('lib-block-v1:(.*)\+type@.*',
                                        'library-v1:\\1', usage_key_string)
            log.error("LIBRARY_KEY_STRING: %s", str(library_key_string))
            library_key = CourseKey.from_string(library_key_string)
            log.error("LIBRARY_KEY: %s", str(library_key))
            library = modulestore().get_library(library_key)
        except InvalidKeyError:
            raise Http404

        xblock = modulestore().get_item(usage_key)
        is_unit_page = is_unit(xblock)
        xblock_info = create_xblock_info(xblock,
                                         include_ancestor_info=False,
                                         graders=[])
        component_templates = get_component_templates(library)
        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()

        # 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

        return render_to_response(
            'library_advanced_component.html',
            {
                #'context_course': course,  # Needed only for display of menus at top of page.
                'context_library':
                library,
                'action':
                action,
                'xblock':
                xblock,
                'xblock_locator':
                xblock.location,
                'unit':
                unit,
                'new_unit_category':
                'vertical',
                'outline_url':
                '{url}?format=concise'.format(
                    url=reverse_library_url('library_handler', library_key)),
                'ancestor_xblocks':
                ancestor_xblocks,
                'component_templates':
                component_templates,
                'xblock_info':
                xblock_info,
                'templates':
                CONTAINER_TEMPLATES
            })

    elif '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',
                    '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")
def export_handler(request, course_key_string):
    """
    The restful handler for exporting a course.

    GET
        html: return html page for import page
        application/x-tgz: return tar.gz file containing exported course
        json: not supported
    POST
        Start a Celery task to export the course

    Note that there are 3 ways to request the tar.gz file.  The Studio UI uses
    a POST request to start the export asynchronously, with a link appearing
    on the page once it's ready.  Additionally, for backwards compatibility
    reasons the request header can specify application/x-tgz via HTTP_ACCEPT,
    or a query parameter can be used (?_accept=application/x-tgz); this will
    export the course synchronously and return the resulting file (unless the
    request times out for a large course).

    If the tar.gz file has been requested but the export operation fails, the
    import page will be returned including a description of the error.
    """
    course_key = CourseKey.from_string(course_key_string)
    if not has_course_author_access(request.user, course_key):
        raise PermissionDenied()

    if isinstance(course_key, LibraryLocator):
        courselike_module = modulestore().get_library(course_key)
        context = {
            'context_library':
            courselike_module,
            'courselike_home_url':
            reverse_library_url("library_handler", course_key),
            'library':
            True
        }
    else:
        courselike_module = modulestore().get_course(course_key)
        if courselike_module is None:
            raise Http404
        context = {
            'context_course': courselike_module,
            'courselike_home_url': reverse_course_url("course_handler",
                                                      course_key),
            'library': False
        }
    context['status_url'] = reverse_course_url('export_status_handler',
                                               course_key)

    # an _accept URL parameter will be preferred over HTTP_ACCEPT in the header.
    requested_format = request.GET.get(
        '_accept', request.META.get('HTTP_ACCEPT', 'text/html'))

    if request.method == 'POST':
        export_olx.delay(request.user.id, course_key_string,
                         request.LANGUAGE_CODE)
        return JsonResponse({'ExportStatus': 1})
    elif 'application/x-tgz' in requested_format:
        try:
            tarball = create_export_tarball(courselike_module, course_key,
                                            context)
            return send_tarball(tarball)
        except SerializationError:
            return render_to_response('export.html', context)
    elif 'text/html' in requested_format:
        return render_to_response('export.html', context)
    else:
        # Only HTML or x-tgz request formats are supported (no JSON).
        return HttpResponse(status=406)
def export_handler(request, course_key_string):
    """
    The restful handler for the export page.

    GET
        html: return html page for import page
    """
    error = request.GET.get("error", None)
    error_message = request.GET.get("error_message", None)
    failed_module = request.GET.get("failed_module", None)
    unit = request.GET.get("unit", None)

    courselike_key = CourseKey.from_string(course_key_string)
    library = isinstance(courselike_key, LibraryLocator)
    if library:
        successful_url = reverse_library_url("library_handler", courselike_key)
        courselike_module = modulestore().get_library(courselike_key)
        context_name = "context_library"
    else:
        successful_url = reverse_course_url("course_handler", courselike_key)
        courselike_module = modulestore().get_course(courselike_key)
        context_name = "context_course"

    if not has_course_author_access(request.user, courselike_key):
        raise PermissionDenied()

    export_url = reverse("course_import_export_handler",
                         kwargs={
                             "course_key_string": unicode(courselike_key),
                         }) + "?accept=application/x-tgz"

    export_url += "&{0}".format(
        urlencode({
            "redirect":
            reverse_course_url("export_handler", unicode(courselike_key))
        }))

    if unit:
        try:
            edit_unit_url = reverse_usage_url("container_handler", unit)
        except (InvalidKeyError, AttributeError):
            log.error("Invalid parent key supplied to export view: %s", unit)

            return render_to_response(
                "export.html", {
                    context_name:
                    courselike_module,
                    "export_url":
                    export_url,
                    "raw_err_msg":
                    _("An invalid parent key was supplied: \"{supplied_key}\" "
                      "is not a valid course unit.").format(supplied_key=unit),
                    "library":
                    library
                })
    else:
        edit_unit_url = ""

    if error:
        return render_to_response(
            'export.html', {
                context_name: courselike_module,
                "export_url": export_url,
                "in_err": error,
                "unit": unit,
                "failed_module": failed_module,
                "edit_unit_url": edit_unit_url,
                "course_home_url": successful_url,
                "raw_err_msg": error_message,
                "library": library
            })
    else:
        return render_to_response(
            "export.html", {
                context_name: courselike_module,
                "export_url": export_url,
                "library": library
            })
Beispiel #35
0
def playlist_handler(request, course_key_string):
    
    #log.info("test from playlist")
    store = modulestore()
    all_libraries = []
    libraries_name = []
    libraries_modules = []
    for library in store.get_libraries():
	all_libraries.append(library)
    for library in all_libraries:
	libraries_name.append(library.display_name)
	#log.info(library.children)
	modules = [library.display_name]
	for children in library.children:
	    display_name = store.get_item(children, 1).display_name
	    modules.append(str(display_name) + "&" + str(children))
	    #log.info(children)
	libraries_modules.append(modules)

    #log.info(libraries_modules)

    course_key = CourseKey.from_string(course_key_string)
    export_url = reverse_course_url('playlist_handler', course_key)
    if not has_course_author_access(request.user, course_key):
        raise PermissionDenied()

    if isinstance(course_key, LibraryLocator):
        courselike_module = modulestore().get_library(course_key)
        context = {
            'context_library': courselike_module,
            'courselike_home_url': reverse_library_url("library_handler", course_key),
            'library': True
        }
    else:
        courselike_module = modulestore().get_course(course_key)
        if courselike_module is None:
            raise Http404
        context = {
            'context_course': courselike_module,
            'courselike_home_url': reverse_course_url("course_handler", course_key),
            'library': False
        }

    context['export_url'] = export_url + '?_accept=application/x-tgz'

    name_string = libraries_name[0]
    for i in range(1,len(libraries_name)):
	name_string += "&" + libraries_name[i]
    context['libraries_display_name'] = name_string
    context['libraries_module'] = libraries_modules

    #log.info(context)

    # an _accept URL parameter will be preferred over HTTP_ACCEPT in the header.
    requested_format = request.GET.get('_accept', request.META.get('HTTP_ACCEPT', 'text/html'))

    if 'application/x-tgz' in requested_format:
        try:
            tarball = create_export_tarball(courselike_module, course_key, context)
        except SerializationError:
            return render_to_response('playlist.html', context)
        return send_tarball(tarball)

    elif 'text/html' in requested_format:
        return render_to_response('playlist.html', context)

    else:
        # Only HTML or x-tgz request formats are supported (no JSON).
        return HttpResponse(status=406)
Beispiel #36
0
def export_handler(request, course_key_string):
    """
    The restful handler for the export page.

    GET
        html: return html page for import page
    """
    error = request.GET.get("error", None)
    error_message = request.GET.get("error_message", None)
    failed_module = request.GET.get("failed_module", None)
    unit = request.GET.get("unit", None)

    courselike_key = CourseKey.from_string(course_key_string)
    library = isinstance(courselike_key, LibraryLocator)
    if library:
        successful_url = reverse_library_url("library_handler", courselike_key)
        courselike_module = modulestore().get_library(courselike_key)
        context_name = "context_library"
    else:
        successful_url = reverse_course_url("course_handler", courselike_key)
        courselike_module = modulestore().get_course(courselike_key)
        context_name = "context_course"

    if not has_course_author_access(request.user, courselike_key):
        raise PermissionDenied()

    export_url = reverse(
        "course_import_export_handler",
        kwargs={
            "course_key_string": unicode(courselike_key),
        }
    ) + "?accept=application/x-tgz"

    export_url += "&{0}".format(
        urlencode({
            "redirect": reverse_course_url(
                "export_handler",
                unicode(courselike_key)
            )
        })
    )

    if unit:
        try:
            edit_unit_url = reverse_usage_url("container_handler", unit)
        except (InvalidKeyError, AttributeError):
            log.error("Invalid parent key supplied to export view: %s", unit)

            return render_to_response("export.html", {
                context_name: courselike_module,
                "export_url": export_url,
                "raw_err_msg": _(
                    "An invalid parent key was supplied: \"{supplied_key}\" "
                    "is not a valid course unit."
                ).format(supplied_key=unit),
                "library": library
            })
    else:
        edit_unit_url = ""

    if error:
        return render_to_response('export.html', {
            context_name: courselike_module,
            "export_url": export_url,
            "in_err": error,
            "unit": unit,
            "failed_module": failed_module,
            "edit_unit_url": edit_unit_url,
            "course_home_url": successful_url,
            "raw_err_msg": error_message,
            "library": library
        })
    else:
        return render_to_response("export.html", {
            context_name: courselike_module,
            "export_url": export_url,
            "library": library
        })