예제 #1
0
def _render_invalid_certificate(request,
                                course_id,
                                platform_name,
                                configuration,
                                cert_path=INVALID_CERTIFICATE_TEMPLATE_PATH):
    """
    Renders the invalid certificate view with default header and footer.
    """
    context = {}
    _update_context_with_basic_info(context, course_id, platform_name,
                                    configuration)
    # Add certificate header/footer data to current context
    context.update(
        get_certificate_header_context(is_secure=request.is_secure()))
    context.update(get_certificate_footer_context())
    return render_to_response(cert_path, context)
예제 #2
0
def render_press_release(request, slug):
    """
    Render a press release given a slug.  Similar to the "render" function above,
    but takes a slug and does a basic conversion to convert it to a template file.
    a) all lower case,
    b) convert dashes to underscores, and
    c) appending ".html"
    """
    template = slug.lower().replace('-', '_') + ".html"
    try:
        resp = render_to_response(
            'static_templates/press_releases/' + template, {})
    except TemplateDoesNotExist:
        raise Http404  # lint-amnesty, pylint: disable=raise-missing-from
    else:
        return resp
예제 #3
0
def _render_valid_certificate(request, context, custom_template=None):
    """
    Renders certificate
    """
    if custom_template:
        template = Template(
            custom_template.template,
            output_encoding='utf-8',
            input_encoding='utf-8',
            default_filters=['decode.utf8'],
            encoding_errors='replace',
        )
        context = RequestContext(request, context)
        return HttpResponse(template.render(context))
    else:
        return render_to_response("certificates/valid.html", context)
예제 #4
0
def _asset_index(request, course_key):
    '''
    Display an editable asset library.

    Supports start (0-based index into the list of assets) and max query parameters.
    '''
    course_module = modulestore().get_course(course_key)

    return render_to_response('asset_index.html', {
        'language_code': request.LANGUAGE_CODE,
        'context_course': course_module,
        'max_file_size_in_mbs': settings.MAX_ASSET_UPLOAD_FILE_SIZE_IN_MB,
        'chunk_size_in_mbs': settings.UPLOAD_CHUNK_SIZE_IN_MB,
        'max_file_size_redirect_url': settings.MAX_ASSET_UPLOAD_FILE_SIZE_URL,
        'asset_callback_url': reverse_course_url('assets_handler', course_key)
    })
예제 #5
0
def program_listing(request):
    """View a list of programs in which the user is engaged."""
    programs_config = ProgramsApiConfig.current()
    programs_fragment = ProgramsFragmentView().render_to_fragment(
        request, programs_config=programs_config)

    context = {
        'disable_courseware_js': True,
        'programs_fragment': programs_fragment,
        'nav_hidden': True,
        'show_dashboard_tabs': True,
        'show_program_listing': programs_config.enabled,
        'uses_bootstrap': True,
    }

    return render_to_response('learner_dashboard/programs.html', context)
예제 #6
0
파일: views.py 프로젝트: sliva/edx-platform
def dashboard(request, course, ccx=None):
    """
    Display the CCX Coach Dashboard.
    """
    # right now, we can only have one ccx per user and course
    # so, if no ccx is passed in, we can sefely redirect to that
    if ccx is None:
        ccx = get_ccx_for_coach(course, request.user)
        if ccx:
            url = reverse(
                'ccx_coach_dashboard',
                kwargs={'course_id': CCXLocator.from_course_locator(course.id, six.text_type(ccx.id))}
            )
            return redirect(url)

    context = {
        'course': course,
        'ccx': ccx,
    }
    context.update(get_ccx_creation_dict(course))

    if ccx:
        ccx_locator = CCXLocator.from_course_locator(course.id, six.text_type(ccx.id))
        # At this point we are done with verification that current user is ccx coach.
        assign_staff_role_to_ccx(ccx_locator, request.user, course.id)
        schedule = get_ccx_schedule(course, ccx)
        grading_policy = get_override_for_ccx(
            ccx, course, 'grading_policy', course.grading_policy)
        context['schedule'] = json.dumps(schedule, indent=4)
        context['save_url'] = reverse(
            'save_ccx', kwargs={'course_id': ccx_locator})
        context['ccx_members'] = CourseEnrollment.objects.filter(course_id=ccx_locator, is_active=True)
        context['gradebook_url'] = reverse(
            'ccx_gradebook', kwargs={'course_id': ccx_locator})
        context['grades_csv_url'] = reverse(
            'ccx_grades_csv', kwargs={'course_id': ccx_locator})
        context['grading_policy'] = json.dumps(grading_policy, indent=4)
        context['grading_policy_url'] = reverse(
            'ccx_set_grading_policy', kwargs={'course_id': ccx_locator})

        with ccx_course(ccx_locator) as course:  # lint-amnesty, pylint: disable=redefined-argument-from-local
            context['course'] = course

    else:
        context['create_ccx_url'] = reverse(
            'create_ccx', kwargs={'course_id': course.id})
    return render_to_response('ccx/coach_dashboard.html', context)
예제 #7
0
파일: tabs.py 프로젝트: yrchen/edx-platform
def tabs_handler(request, course_key_string):
    """
    The restful handler for static tabs.

    GET
        html: return page for editing static tabs
        json: not supported
    PUT or POST
        json: update the tab order. It is expected that the request body contains a JSON-encoded dict with entry "tabs".
        The value for "tabs" is an array of tab locators, indicating the desired order of the tabs.

    Creating a tab, deleting a tab, or changing its contents is not supported through this method.
    Instead use the general xblock URL (see item.xblock_handler).
    """
    course_key = CourseKey.from_string(course_key_string)
    if not has_course_author_access(request.user, course_key):
        raise PermissionDenied()

    course_item = modulestore().get_course(course_key)

    if "application/json" in request.META.get("HTTP_ACCEPT",
                                              "application/json"):
        if request.method == "GET":  # lint-amnesty, pylint: disable=no-else-raise
            raise NotImplementedError("coming soon")
        else:
            try:
                update_tabs_handler(course_item, request.json, request.user)
            except ValidationError as err:
                return JsonResponseBadRequest(err.detail)
            return JsonResponse()

    elif request.method == "GET":  # assume html
        # get all tabs from the tabs list: static tabs (a.k.a. user-created tabs) and built-in tabs
        # present in the same order they are displayed in LMS

        tabs_to_render = list(get_course_tabs(course_item, request.user))

        return render_to_response(
            "edit-tabs.html",
            {
                "context_course": course_item,
                "tabs_to_render": tabs_to_render,
                "lms_link": get_lms_link_for_item(course_item.location),
            },
        )
    else:
        return HttpResponseNotFound()
예제 #8
0
    def render(self, request):
        """
        Render the index page.
        """
        self._prefetch_and_bind_course(request)

        if self.course.has_children_at_depth(CONTENT_DEPTH):
            self._reset_section_to_exam_if_required()
            self.chapter = self._find_chapter()
            self.section = self._find_section()

            if self.chapter and self.section:
                self._redirect_if_not_requested_section()
                self._save_positions()
                self._prefetch_and_bind_section()
                self._redirect_to_learning_mfe()

            check_content_start_date_for_masquerade_user(self.course_key, self.effective_user, request,
                                                         self.course.start, self.chapter.start, self.section.start)

        if not request.user.is_authenticated:
            qs = urllib.parse.urlencode({
                'course_id': self.course_key,
                'enrollment_action': 'enroll',
                'email_opt_in': False,
            })

            allow_anonymous = check_public_access(self.course, [COURSE_VISIBILITY_PUBLIC])

            if not allow_anonymous:
                PageLevelMessages.register_warning_message(
                    request,
                    Text(_("You are not signed in. To see additional course content, {sign_in_link} or "
                           "{register_link}, and enroll in this course.")).format(
                        sign_in_link=HTML('<a href="{url}">{sign_in_label}</a>').format(
                            sign_in_label=_('sign in'),
                            url='{}?{}'.format(reverse('signin_user'), qs),
                        ),
                        register_link=HTML('<a href="/{url}">{register_label}</a>').format(
                            register_label=_('register'),
                            url='{}?{}'.format(reverse('register_user'), qs),
                        ),
                    )
                )

        return render_to_response('courseware/courseware.html', self._create_courseware_context(request))
예제 #9
0
def edxnotes(request, course_id):
    """
    Displays the EdxNotes page.

    Arguments:
        request: HTTP request object
        course_id: course id

    Returns:
        Rendered HTTP response.
    """
    course_key = CourseKey.from_string(course_id)
    course = get_course_with_access(request.user, "load", course_key)

    if not is_feature_enabled(course, request.user):
        raise Http404

    notes_info = get_notes(request, course)
    has_notes = (len(notes_info.get('results')) > 0)
    context = {
        "course": course,
        "notes_endpoint": reverse("notes", kwargs={"course_id": course_id}),
        "notes": notes_info,
        "page_size": DEFAULT_PAGE_SIZE,
        "debug": settings.DEBUG,
        'position': None,
        'disabled_tabs': settings.NOTES_DISABLED_TABS,
        'has_notes': has_notes,
    }

    if not has_notes:
        field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
            course.id, request.user, course, depth=2)
        course_module = get_module_for_descriptor(request.user,
                                                  request,
                                                  course,
                                                  field_data_cache,
                                                  course_key,
                                                  course=course)
        position = get_course_position(course_module)
        if position:
            context.update({
                'position': position,
            })

    return render_to_response("edxnotes/edxnotes.html", context)
예제 #10
0
def program_details(request, program_uuid):
    """View details about a specific program."""
    programs_config = ProgramsApiConfig.current()
    program_fragment = ProgramDetailsFragmentView().render_to_fragment(
        request, program_uuid, programs_config=programs_config
    )

    context = {
        'program_fragment': program_fragment,
        'show_program_listing': programs_config.enabled,
        'show_dashboard_tabs': True,
        'nav_hidden': True,
        'disable_courseware_js': True,
        'uses_bootstrap': True,
    }

    return render_to_response('learner_dashboard/program_details.html', context)
예제 #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":
            str(library.location.library_key),
            "version":
            str(library.runtime.course_entry.course_key.version_guid),
            "previous_version":
            str(prev_version) if prev_version else None,
            "blocks": [str(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': component_templates,
            'xblock_info': xblock_info,
            'templates': CONTAINER_TEMPLATES,
        })
예제 #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)
    def get(self, request):
        """
        Based on the query string parameters passed through the GET request
        Search the data store for information about ProgramEnrollment and
        SSO linkage with the user.
        """
        search_error = ''
        edx_username_or_email = request.GET.get('edx_user', '').strip()
        org_key = request.GET.get('org_key', '').strip()
        external_user_key = request.GET.get('external_user_key', '').strip()
        learner_program_enrollments = {}
        saml_providers_with_org_key = self._get_org_keys_and_idps()
        selected_provider = None
        if org_key:
            selected_provider = saml_providers_with_org_key.get(org_key)
        if edx_username_or_email:
            learner_program_enrollments, search_error = self._get_account_info(
                edx_username_or_email,
                selected_provider,
            )
        elif org_key and external_user_key:
            learner_program_enrollments = self._get_external_user_info(
                external_user_key,
                org_key,
                selected_provider,
            )
            if not learner_program_enrollments:
                search_error = 'No user found for external key {} for institution {}'.format(
                    external_user_key, org_key)
        elif not org_key and not external_user_key:
            # This is initial rendering state.
            pass
        else:
            search_error = (
                "To perform a search, you must provide either the student's "
                "(a) edX username, "
                "(b) email address associated with their edX account, or "
                "(c) Identity-providing institution and external key!")

        return render_to_response(
            self.CONSOLE_TEMPLATE_PATH, {
                'error': search_error,
                'learner_program_enrollments': learner_program_enrollments,
                'org_keys': sorted(saml_providers_with_org_key.keys()),
            })
예제 #14
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':
            str(library_key),
            'lib_users_url':
            reverse_library_url('manage_library_users', library_key_string),
            'show_children_previews':
            library.show_children_previews
        })
예제 #15
0
def checklists_handler(request, course_key_string=None):
    '''
    The restful handler for course checklists.
    It allows retrieval of the checklists (as an HTML page).

    GET
        html: return an html page which will show course checklists. Note that only the checklists container
            is returned and that the actual data is determined with a client-side request.
    '''
    course_key = CourseKey.from_string(course_key_string)
    if not has_course_author_access(request.user, course_key):
        raise PermissionDenied()

    course_module = modulestore().get_course(course_key)
    return render_to_response('checklists.html', {
        'language_code': request.LANGUAGE_CODE,
        'context_course': course_module,
    })
예제 #16
0
 def post(self, request, catalog_id):
     """Update or delete this catalog."""
     client = self.get_catalog_api_client(request.user)
     if request.POST.get('delete-catalog') == 'on':
         client.catalogs(catalog_id).delete()
         return redirect(reverse('api_admin:catalog-search'))
     form = CatalogForm(request.POST)
     if not form.is_valid():
         response = client.catalogs(catalog_id).get()
         catalog = Catalog(attributes=response)
         return render_to_response(self.template_name,
                                   self.get_context_data(
                                       catalog, form, client),
                                   status=400)
     catalog = client.catalogs(catalog_id).patch(form.cleaned_data)
     return redirect(
         reverse('api_admin:catalog-edit',
                 kwargs={'catalog_id': catalog['id']}))
예제 #17
0
 def inner(request, *args, **kwargs):
     """
     Execute the function in try..except block and return custom server-error page in case of unhandled exception
     """
     try:
         return func(request, *args, **kwargs)
     except Exception:  # pylint: disable=broad-except
         if settings.DEBUG:  # lint-amnesty, pylint: disable=no-else-raise
             # In debug mode let django process the 500 errors and display debug info for the developer
             raise
         elif test_func is None or test_func(request):
             # Display custom 500 page if either
             #   1. test_func is None (meaning nothing to test)
             #   2. or test_func(request) returns True
             log.exception("Error in django view.")
             return render_to_response(template_path, context)
         else:
             # Do not show custom 500 error when test fails
             raise
예제 #18
0
    def post(self, request, username):
        """
        Create a new catalog for a user.
        """
        form = CatalogForm(request.POST)
        client = self.get_catalog_api_client(request.user)
        if not form.is_valid():
            return render_to_response(self.template,
                                      self.get_context_data(
                                          client, username, form),
                                      status=400)

        attrs = form.cleaned_data
        response = client.post(self.catalogs_api_url, data=attrs)
        response.raise_for_status()
        catalog = response.json()
        return redirect(
            reverse('api_admin:catalog-edit',
                    kwargs={'catalog_id': catalog['id']}))
예제 #19
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':  # lint-amnesty, pylint: disable=no-else-raise
            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 get(self, request):  # lint-amnesty, pylint: disable=missing-function-docstring
        # If ZENDESK_URL is not defined, then it will redirect to the static contact page
        # to avoid 500 error that arises due to missing Zendesk configuration
        if not settings.ZENDESK_URL:
            return redirect('contact')

        if not configuration_helpers.get_value('CONTACT_US_PAGE', True):
            raise Http404

        context = {
            'platform_name':
            configuration_helpers.get_value('platform_name',
                                            settings.PLATFORM_NAME),
            'support_email':
            configuration_helpers.get_value('CONTACT_EMAIL',
                                            settings.CONTACT_EMAIL),
            'custom_fields':
            settings.ZENDESK_CUSTOM_FIELDS
        }

        # Tag all issues with LMS to distinguish channel which received the request
        tags = ['LMS']

        # Per edX support, we would like to be able to route feedback items by site via tagging
        current_site_name = configuration_helpers.get_value("SITE_NAME")
        if current_site_name:
            current_site_name = current_site_name.replace(".", "_")
            tags.append(f"site_name_{current_site_name}")

        if request.user.is_authenticated:
            context['course_id'] = request.session.get('course_id', '')
            context[
                'user_enrollments'] = CourseEnrollment.enrollments_for_user_with_overviews_preload(
                    request.user)
            enterprise_customer = enterprise_api.enterprise_customer_for_request(
                request)
            if enterprise_customer:
                tags.append('enterprise_learner')

        context['tags'] = tags

        return render_to_response("support/contact_us.html", context)
예제 #21
0
def spoc_gradebook(request, course_id):
    """
    Show the gradebook for this course:
    - Only shown for courses with enrollment < settings.FEATURES.get("MAX_ENROLLMENT_INSTR_BUTTONS")
    - Only displayed to course staff
    """
    course_key = CourseKey.from_string(course_id)
    course = get_course_with_access(request.user, 'staff', course_key, depth=None)
    student_info, page = get_grade_book_page(request, course, course_key)

    return render_to_response('courseware/gradebook.html', {
        'page': page,
        'page_url': reverse('spoc_gradebook', kwargs={'course_id': str(course_key)}),
        'students': student_info,
        'course': course,
        'course_id': course_key,
        # Checked above
        'staff_access': True,
        'ordered_grades': sorted(list(course.grade_cutoffs.items()), key=lambda i: i[1], reverse=True),
    })
예제 #22
0
파일: views.py 프로젝트: sliva/edx-platform
def html_index(request, course_id, book_index, chapter=None):
    """
    Display an HTML textbook.

    course_id: course for which to display text.  The course should have
      "html_textbooks" property defined.

    book index:  zero-based index of which HTML textbook to display.

    chapter:  (optional) one-based index into the chapter array of textbook HTML files to display.
        Defaults to first chapter.  Specifying this assumes that there are separate HTML files for
        each chapter in a textbook.
    """
    course_key = CourseKey.from_string(course_id)
    course = get_course_with_access(request.user, 'load', course_key)
    staff_access = bool(has_access(request.user, 'staff', course))

    book_index = int(book_index)
    if book_index < 0 or book_index >= len(course.html_textbooks):
        raise Http404(u"Invalid book index value: {0}".format(book_index))
    textbook = course.html_textbooks[book_index]

    if 'url' in textbook:
        textbook['url'] = remap_static_url(textbook['url'], course)
    # then remap all the chapter URLs as well, if they are provided.
    if 'chapters' in textbook:
        for entry in textbook['chapters']:
            entry['url'] = remap_static_url(entry['url'], course)

    student = request.user
    return render_to_response(
        'static_htmlbook.html',
        {
            'book_index': book_index,
            'course': course,
            'textbook': textbook,
            'chapter': chapter,
            'student': student,
            'staff_access': staff_access,
        },
    )
예제 #23
0
파일: views.py 프로젝트: sliva/edx-platform
def ccx_gradebook(request, course, ccx=None):
    """
    Show the gradebook for this CCX.
    """
    if not ccx:
        raise Http404

    ccx_key = CCXLocator.from_course_locator(course.id, six.text_type(ccx.id))
    with ccx_course(ccx_key) as course:  # lint-amnesty, pylint: disable=redefined-argument-from-local
        student_info, page = get_grade_book_page(request, course, course_key=ccx_key)

        return render_to_response('courseware/gradebook.html', {
            'page': page,
            'page_url': reverse('ccx_gradebook', kwargs={'course_id': ccx_key}),
            'students': student_info,
            'course': course,
            'course_id': course.id,
            'staff_access': request.user.is_staff,
            'ordered_grades': sorted(
                list(course.grade_cutoffs.items()), key=lambda i: i[1], reverse=True),
        })
예제 #24
0
def manage_user_standing(request):
    """
    Renders the view used to manage user standing. Also displays a table
    of user accounts that have been disabled and who disabled them.
    """
    if not request.user.is_staff:
        raise Http404
    all_disabled_accounts = UserStanding.objects.filter(
        account_status=UserStanding.ACCOUNT_DISABLED)

    all_disabled_users = [standing.user for standing in all_disabled_accounts]

    headers = ['username', 'account_changed_by']
    rows = []
    for user in all_disabled_users:
        row = [user.username, user.standing.changed_by]
        rows.append(row)

    context = {'headers': headers, 'rows': rows}

    return render_to_response("manage_user_standing.html", context)
예제 #25
0
def custon_certificate_view(request,certificate_uuid):
    context = {}
    try:
        certificate = GeneratedCertificate.eligible_certificates.get(
            verify_uuid=certificate_uuid,
            status=CertificateStatuses.downloadable
        )
        course_overview = CourseOverview.objects.get(id=certificate.course_id)
        user = User.objects.get(id=certificate.user.id)
        course_exinfo = course_extrainfo.objects.get(course_id=course_overview.id)
        category_name = categories.objects.get(id=course_exinfo.category)
        context['certificate_username'] = user.username
        context['certificate_created'] = certificate.created_date
        context['course_name'] = course_overview.display_name
        context['category'] = category_name
        certificate_img_obj = CustomCertificatePath.objects.filter(verify_uuid=certificate_uuid)
        if certificate_img_obj:
            context['certificate_img_path'] = certificate_img_obj[0].file_path
        else:
            context['certificate_img_path'] = ''
        if course_exinfo.microsite_visibile_only == '0':
            context['course_about_url'] = 'https://docmode.org/' + course_exinfo.course_seo_url
        else:
            site_config_obj = SiteConfiguration.objects.filter(site=request.site)
            if site_config_obj:
                site_url  = site_config_obj[0].get_value('MKTG_URLS')
                sites_url = site_url['ROOT']
                context['course_about_url'] = sites_url +'/view/' + course_exinfo.course_seo_url
            else:
                context['course_about_url'] = 'https://docmode.org/' + course_exinfo.course_seo_url
        context['course_enrollments'] = len(CourseEnrollment.objects.filter(course_id=course_overview.id))
        if request.user.id == user.id:
            context['certificate_url'] = reverse('certificates:render_cert_by_uuid', kwargs={'certificate_uuid': certificate_uuid})
            context['global_share_option_url'] = settings.LMS_ROOT_URL + '/accomplishments/' + certificate_uuid
        else:
            context['certificate_url'] = ''
            context['global_share_option_url'] = ''
        return render_to_response("certificates/custom_certificate.html", context)
    except GeneratedCertificate.DoesNotExist:
        raise Http404
예제 #26
0
    def get(self, request):  # lint-amnesty, pylint: disable=arguments-differ
        """Displays course Enrollment and staffing course statistics"""

        if not request.user.is_staff:
            raise Http404
        data = []

        for course in self.get_courses():
            datum = [course.display_name, course.id]
            datum += [
                CourseEnrollment.objects.filter(course_id=course.id).count()
            ]
            datum += [CourseStaffRole(course.id).users_with_role().count()]
            datum += [
                ','.join([
                    x.username
                    for x in CourseInstructorRole(course.id).users_with_role()
                ])
            ]
            data.append(datum)

        datatable = dict(header=[
            _('Course Name'),
            _('course_id'),
            _('# enrolled'),
            _('# staff'),
            _('instructors')
        ],
                         title=_('Enrollment information for all courses'),
                         data=data)
        context = {
            'datatable': datatable,
            'msg': self.msg,
            'djangopid': os.getpid(),
            'modeflag': {
                'staffing': 'active-section'
            },
        }
        return render_to_response(self.template_name, context)
예제 #27
0
    def get(self, request):
        if not configuration_helpers.get_value('CONTACT_US_PAGE', True):
            raise Http404

        context = {
            'platform_name':
            configuration_helpers.get_value('platform_name',
                                            settings.PLATFORM_NAME),
            'support_email':
            configuration_helpers.get_value('CONTACT_EMAIL',
                                            settings.CONTACT_EMAIL),
            'custom_fields':
            settings.ZENDESK_CUSTOM_FIELDS
        }

        # Tag all issues with LMS to distinguish channel which received the request
        tags = ['LMS']

        # Per edX support, we would like to be able to route feedback items by site via tagging
        current_site_name = configuration_helpers.get_value("SITE_NAME")
        if current_site_name:
            current_site_name = current_site_name.replace(".", "_")
            tags.append("site_name_{site}".format(site=current_site_name))

        if request.user.is_authenticated:
            context['course_id'] = request.session.get('course_id', '')
            context[
                'user_enrollments'] = CourseEnrollment.enrollments_for_user_with_overviews_preload(
                    request.user)
            enterprise_customer = enterprise_api.enterprise_customer_for_request(
                request)
            if enterprise_customer:
                tags.append('enterprise_learner')

        context['tags'] = tags

        return render_to_response("support/contact_us.html", context)
예제 #28
0
def render(request, template):
    """
    This view function renders the template sent without checking that it
    exists. Do not expose template as a regex part of the url. The user should
    not be able to ender any arbitray template name. The correct usage would be:

    url(r'^jobs$', 'static_template_view.views.render', {'template': 'jobs.html'}, name="jobs")
    """

    # Guess content type from file extension
    content_type, __ = mimetypes.guess_type(template)

    try:
        context = {}
        # This is necessary for the dialog presented with the TOS in /register
        if template == 'honor.html':
            context['allow_iframing'] = True
        # Format Examples: static_template_about_header
        configuration_base = 'static_template_' + template.replace(
            '.html', '').replace('-', '_')
        page_header = configuration_helpers.get_value(configuration_base +
                                                      '_header')
        page_content = configuration_helpers.get_value(configuration_base +
                                                       '_content')
        if page_header:
            context['page_header'] = mark_safe(page_header)
        if page_content:
            context['page_content'] = mark_safe(page_content)
        result = render_to_response('static_templates/' + template,
                                    context,
                                    content_type=content_type)
        return result
    except TopLevelLookupException:
        raise Http404  # lint-amnesty, pylint: disable=raise-missing-from
    except TemplateDoesNotExist:
        raise Http404  # lint-amnesty, pylint: disable=raise-missing-from
def create_ccx(request, course, ccx=None):
    """
    Create a new CCX
    """
    name = request.POST.get('name')

    if hasattr(course, 'ccx_connector') and course.ccx_connector:
        # if ccx connector url is set in course settings then inform user that he can
        # only create ccx by using ccx connector url.
        context = get_ccx_creation_dict(course)
        messages.error(request, context['use_ccx_con_error_message'])
        return render_to_response('ccx/coach_dashboard.html', context)

    # prevent CCX objects from being created for deprecated course ids.
    if course.id.deprecated:
        messages.error(
            request,
            _("You cannot create a CCX from a course using a deprecated id. "
              "Please create a rerun of this course in the studio to allow "
              "this action."))
        url = reverse('ccx_coach_dashboard', kwargs={'course_id': course.id})
        return redirect(url)

    ccx = CustomCourseForEdX(course_id=course.id,
                             coach=request.user,
                             display_name=name)
    ccx.save()

    # Make sure start/due are overridden for entire course
    start = TODAY().replace(tzinfo=pytz.UTC)
    override_field_for_ccx(ccx, course, 'start', start)
    override_field_for_ccx(ccx, course, 'due', None)

    # Enforce a static limit for the maximum amount of students that can be enrolled
    override_field_for_ccx(ccx, course, 'max_student_enrollments_allowed',
                           settings.CCX_MAX_STUDENTS_ALLOWED)
    # Save display name explicitly
    override_field_for_ccx(ccx, course, 'display_name', name)

    # Hide anything that can show up in the schedule
    hidden = 'visible_to_staff_only'
    for chapter in course.get_children():
        override_field_for_ccx(ccx, chapter, hidden, True)
        for sequential in chapter.get_children():
            override_field_for_ccx(ccx, sequential, hidden, True)
            for vertical in sequential.get_children():
                override_field_for_ccx(ccx, vertical, hidden, True)

    ccx_id = CCXLocator.from_course_locator(course.id, str(ccx.id))

    # Create forum roles
    seed_permissions_roles(ccx_id)
    # Assign administrator forum role to CCX coach
    assign_role(ccx_id, request.user, FORUM_ROLE_ADMINISTRATOR)

    url = reverse('ccx_coach_dashboard', kwargs={'course_id': ccx_id})

    # Enroll the coach in the course
    email_params = get_email_params(course,
                                    auto_enroll=True,
                                    course_key=ccx_id,
                                    display_name=ccx.display_name)
    enroll_email(
        course_id=ccx_id,
        student_email=request.user.email,
        auto_enroll=True,
        email_students=True,
        email_params=email_params,
    )

    assign_staff_role_to_ccx(ccx_id, request.user, course.id)
    add_master_course_staff_to_ccx(course, ccx_id, ccx.display_name)

    # using CCX object as sender here.
    responses = SignalHandler.course_published.send(
        sender=ccx,
        course_key=CCXLocator.from_course_locator(course.id, str(ccx.id)))
    for rec, response in responses:
        log.info(
            'Signal fired when course is published. Receiver: %s. Response: %s',
            rec, response)

    return redirect(url)
예제 #30
0
def student_dashboard(request):
    """
    Provides the LMS dashboard view

    TODO: This is lms specific and does not belong in common code.
    Note:
        To load the all courses set course_limit=None as parameter in GET. If its not None then default course
        limit will be used  that is set in configuration
    Arguments:
        request: The request object.

    Returns:
        The dashboard response.

    """
    user = request.user

    #Added by Mahendra
    if request.is_ajax():
        if request.method == 'GET' :
            if user.is_authenticated:
                if request.GET.get('disclaimer'):
                    log.info(u'disclaimer %s', request.GET.get('disclaimer'))
                    usr = request.user.id
                    cid = request.GET.get('cid')
                    disclaimer = disclaimer_agreement_status(course_id=cid, user_id=usr, status='1')
                    disclaimer.save()
                else:
                    usr = request.user.id
                    cid = request.GET.get('cid')
                    view_counter = user_view_counter.objects.filter(course_id=cid, user=usr)
                    if view_counter :
                        update_counter = user_view_counter.objects.filter(
                            course_id=cid,
                            user=usr
                        ).update(counter = F('counter')+1)
                    else:
                        countr = user_view_counter(user_id=usr, course_id=cid,counter=1)
                        countr.save()

    if not UserProfile.objects.filter(user=user).exists():
        return redirect(reverse('account_settings'))

    platform_name = configuration_helpers.get_value("platform_name", settings.PLATFORM_NAME)

    enable_verified_certificates = configuration_helpers.get_value(
        'ENABLE_VERIFIED_CERTIFICATES',
        settings.FEATURES.get('ENABLE_VERIFIED_CERTIFICATES')
    )
    display_course_modes_on_dashboard = configuration_helpers.get_value(
        'DISPLAY_COURSE_MODES_ON_DASHBOARD',
        settings.FEATURES.get('DISPLAY_COURSE_MODES_ON_DASHBOARD', True)
    )
    activation_email_support_link = configuration_helpers.get_value(
        'ACTIVATION_EMAIL_SUPPORT_LINK', settings.ACTIVATION_EMAIL_SUPPORT_LINK
    ) or settings.SUPPORT_SITE_LINK
    hide_dashboard_courses_until_activated = configuration_helpers.get_value(
        'HIDE_DASHBOARD_COURSES_UNTIL_ACTIVATED',
        settings.FEATURES.get('HIDE_DASHBOARD_COURSES_UNTIL_ACTIVATED', False)
    )
    empty_dashboard_message = configuration_helpers.get_value(
        'EMPTY_DASHBOARD_MESSAGE', None
    )

    disable_course_limit = request and 'course_limit' in request.GET
    course_limit = get_dashboard_course_limit() if not disable_course_limit else None

    # Get the org whitelist or the org blacklist for the current site
    site_org_whitelist, site_org_blacklist = get_org_black_and_whitelist_for_site()
    course_enrollments = list(get_course_enrollments(user, site_org_whitelist, site_org_blacklist, course_limit))

    #Added by dev below code to display enrolled courses in course start date order
    from lms.djangoapps.course_extrainfo.models import course_extrainfo

    new_enrollments = []
    #log.info('course_enrollments--> %s', course_enrollments)
    for enrollment in course_enrollments:
        new_enrollments.append(enrollment.course_overview.id)

    get_type_courses = course_extrainfo.objects.filter(course_id__in=new_enrollments,course_type=1)

    course_ids = []
    for course in get_type_courses:
        course_ids.append(course.course_id)

    courseslist = CourseOverview.objects.filter(pk__in=course_ids).order_by('-start')

    get_type_lectures = course_extrainfo.objects.filter(course_id__in=new_enrollments,course_type=2)

    lecture_ids = []
    for lecture in get_type_lectures:
        lecture_ids.append(lecture.course_id)

    lectureslist = CourseOverview.objects.filter(pk__in=lecture_ids).order_by('-start')
    #code ends here

    # Get the entitlements for the user and a mapping to all available sessions for that entitlement
    # If an entitlement has no available sessions, pass through a mock course overview object
    (course_entitlements,
     course_entitlement_available_sessions,
     unfulfilled_entitlement_pseudo_sessions) = get_filtered_course_entitlements(
        user,
        site_org_whitelist,
        site_org_blacklist
    )

    # Record how many courses there are so that we can get a better
    # understanding of usage patterns on prod.
    monitoring_utils.accumulate('num_courses', len(course_enrollments))

    # Sort the enrollment pairs by the enrollment date
    course_enrollments.sort(key=lambda x: x.created, reverse=True)

    # Retrieve the course modes for each course
    enrolled_course_ids = [enrollment.course_id for enrollment in course_enrollments]
    __, unexpired_course_modes = CourseMode.all_and_unexpired_modes_for_courses(enrolled_course_ids)
    course_modes_by_course = {
        course_id: {
            mode.slug: mode
            for mode in modes
        }
        for course_id, modes in iteritems(unexpired_course_modes)
    }

    # Check to see if the student has recently enrolled in a course.
    # If so, display a notification message confirming the enrollment.
    enrollment_message = _create_recent_enrollment_message(
        course_enrollments, course_modes_by_course
    )
    course_optouts = Optout.objects.filter(user=user).values_list('course_id', flat=True)

    # Display activation message
    activate_account_message = ''
    if not user.is_active:
        activate_account_message = Text(_(
            "Check your {email_start}{email}{email_end} inbox for an account activation link from {platform_name}. "
            "If you need help, contact {link_start}{platform_name} Support{link_end}."
        )).format(
            platform_name=platform_name,
            email_start=HTML("<strong>"),
            email_end=HTML("</strong>"),
            email=user.email,
            link_start=HTML("<a target='_blank' href='{activation_email_support_link}'>").format(
                activation_email_support_link=activation_email_support_link,
            ),
            link_end=HTML("</a>"),
        )

    enterprise_message = get_dashboard_consent_notification(request, user, course_enrollments)

    # Display a message guiding the user to their Enterprise's Learner Portal if enabled
    enterprise_learner_portal_enabled_message = get_enterprise_learner_portal_enabled_message(request)

    recovery_email_message = recovery_email_activation_message = None
    if is_secondary_email_feature_enabled():
        try:
            pending_email = PendingSecondaryEmailChange.objects.get(user=user)
        except PendingSecondaryEmailChange.DoesNotExist:
            try:
                account_recovery_obj = AccountRecovery.objects.get(user=user)
            except AccountRecovery.DoesNotExist:
                recovery_email_message = Text(
                    _(
                        "Add a recovery email to retain access when single-sign on is not available. "
                        "Go to {link_start}your Account Settings{link_end}.")
                ).format(
                    link_start=HTML("<a href='{account_setting_page}'>").format(
                        account_setting_page=reverse('account_settings'),
                    ),
                    link_end=HTML("</a>")
                )
        else:
            recovery_email_activation_message = Text(
                _(
                    "Recovery email is not activated yet. "
                    "Kindly visit your email and follow the instructions to activate it."
                )
            )


    # Disable lookup of Enterprise consent_required_course due to ENT-727
    # Will re-enable after fixing WL-1315
    consent_required_courses = set()
    enterprise_customer_name = None

    # Account activation message
    account_activation_messages = [
        message for message in messages.get_messages(request) if 'account-activation' in message.tags
    ]

    # Global staff can see what courses encountered an error on their dashboard
    staff_access = False
    errored_courses = {}
    if has_access(user, 'staff', 'global'):
        # Show any courses that encountered an error on load
        staff_access = True
        errored_courses = modulestore().get_errored_courses()

    show_courseware_links_for = {
        enrollment.course_id: has_access(request.user, 'load', enrollment.course_overview)
        for enrollment in course_enrollments
    }

    # Find programs associated with course runs being displayed. This information
    # is passed in the template context to allow rendering of program-related
    # information on the dashboard.
    meter = ProgramProgressMeter(request.site, user, enrollments=course_enrollments)
    ecommerce_service = EcommerceService()
    inverted_programs = meter.invert_programs()

    urls, programs_data = {}, {}
    bundles_on_dashboard_flag = WaffleFlag(experiments_namespace, u'bundles_on_dashboard', __name__)

    # TODO: Delete this code and the relevant HTML code after testing LEARNER-3072 is complete
    if bundles_on_dashboard_flag.is_enabled() and inverted_programs and list(inverted_programs.items()):
        if len(course_enrollments) < 4:
            for program in inverted_programs.values():
                try:
                    program_uuid = program[0]['uuid']
                    program_data = get_programs(uuid=program_uuid)
                    program_data = ProgramDataExtender(program_data, request.user).extend()
                    skus = program_data.get('skus')
                    checkout_page_url = ecommerce_service.get_checkout_page_url(*skus)
                    program_data['completeProgramURL'] = checkout_page_url + '&bundle=' + program_data.get('uuid')
                    programs_data[program_uuid] = program_data
                except:  # pylint: disable=bare-except
                    pass

    # Construct a dictionary of course mode information
    # used to render the course list.  We re-use the course modes dict
    # we loaded earlier to avoid hitting the database.
    course_mode_info = {
        enrollment.course_id: complete_course_mode_info(
            enrollment.course_id, enrollment,
            modes=course_modes_by_course[enrollment.course_id]
        )
        for enrollment in course_enrollments
    }

    # Determine the per-course verification status
    # This is a dictionary in which the keys are course locators
    # and the values are one of:
    #
    # VERIFY_STATUS_NEED_TO_VERIFY
    # VERIFY_STATUS_SUBMITTED
    # VERIFY_STATUS_APPROVED
    # VERIFY_STATUS_MISSED_DEADLINE
    #
    # Each of which correspond to a particular message to display
    # next to the course on the dashboard.
    #
    # If a course is not included in this dictionary,
    # there is no verification messaging to display.
    verify_status_by_course = check_verify_status_by_course(user, course_enrollments)
    cert_statuses = {
        enrollment.course_id: cert_info(request.user, enrollment.course_overview)
        for enrollment in course_enrollments
    }

    # only show email settings for Mongo course and when bulk email is turned on
    show_email_settings_for = frozenset(
        enrollment.course_id for enrollment in course_enrollments if (
            is_bulk_email_feature_enabled(enrollment.course_id)
        )
    )

    # Verification Attempts
    # Used to generate the "you must reverify for course x" banner
    verification_status = IDVerificationService.user_status(user)
    verification_errors = get_verification_error_reasons_for_display(verification_status['error'])

    # Gets data for midcourse reverifications, if any are necessary or have failed
    statuses = ["approved", "denied", "pending", "must_reverify"]
    reverifications = reverification_info(statuses)

    enrolled_courses_either_paid = frozenset(
        enrollment.course_id for enrollment in course_enrollments
        if enrollment.is_paid_course()
    )

    # If there are *any* denied reverifications that have not been toggled off,
    # we'll display the banner
    denied_banner = any(item.display for item in reverifications["denied"])

    # get list of courses having pre-requisites yet to be completed
    courses_having_prerequisites = frozenset(
        enrollment.course_id for enrollment in course_enrollments
        if enrollment.course_overview.pre_requisite_courses
    )
    courses_requirements_not_met = get_pre_requisite_courses_not_completed(user, courses_having_prerequisites)

    site_domain = request.site
    if 'notlive' in request.GET:
        if 'viatris' in str(site_domain):
            redirect_message = _("The webinar you are looking for does not start until {date}.").format(
                date=request.GET['notlive']
            )
        else:
            redirect_message = _("The course you are looking for does not start until {date}.").format(
                date=request.GET['notlive']
            )

    elif 'course_closed' in request.GET:
        redirect_message = _("The course you are looking for is closed for enrollment as of {date}.").format(
            date=request.GET['course_closed']
        )
    elif 'access_response_error' in request.GET:
        # This can be populated in a generalized way with fields from access response errors
        redirect_message = request.GET['access_response_error']
    else:
        redirect_message = ''

    valid_verification_statuses = ['approved', 'must_reverify', 'pending', 'expired']
    display_sidebar_on_dashboard = verification_status['status'] in valid_verification_statuses and \
        verification_status['should_display']

    # Filter out any course enrollment course cards that are associated with fulfilled entitlements
    for entitlement in [e for e in course_entitlements if e.enrollment_course_run is not None]:
        course_enrollments = [
            enr for enr in course_enrollments if entitlement.enrollment_course_run.course_id != enr.course_id
        ]

    context = {
        'urls': urls,
        'programs_data': programs_data,
        'enterprise_message': enterprise_message,
        'consent_required_courses': consent_required_courses,
        'enterprise_customer_name': enterprise_customer_name,
        'enrollment_message': enrollment_message,
        'redirect_message': Text(redirect_message),
        'account_activation_messages': account_activation_messages,
        'activate_account_message': activate_account_message,
        'course_enrollments': course_enrollments,
        'course_entitlements': course_entitlements,
        'course_entitlement_available_sessions': course_entitlement_available_sessions,
        'unfulfilled_entitlement_pseudo_sessions': unfulfilled_entitlement_pseudo_sessions,
        'course_optouts': course_optouts,
        'staff_access': staff_access,
        'errored_courses': errored_courses,
        'show_courseware_links_for': show_courseware_links_for,
        'all_course_modes': course_mode_info,
        'cert_statuses': cert_statuses,
        'credit_statuses': _credit_statuses(user, course_enrollments),
        'show_email_settings_for': show_email_settings_for,
        'reverifications': reverifications,
        'verification_display': verification_status['should_display'],
        'verification_status': verification_status['status'],
        'verification_expiry': verification_status['verification_expiry'],
        'verification_status_by_course': verify_status_by_course,
        'verification_errors': verification_errors,
        'denied_banner': denied_banner,
        'billing_email': settings.PAYMENT_SUPPORT_EMAIL,
        'user': user,
        'logout_url': reverse('logout'),
        'platform_name': platform_name,
        'enrolled_courses_either_paid': enrolled_courses_either_paid,
        'provider_states': [],
        'courses_requirements_not_met': courses_requirements_not_met,
        'nav_hidden': True,
        'inverted_programs': inverted_programs,
        'show_program_listing': ProgramsApiConfig.is_enabled(),
        'show_dashboard_tabs': True,
        'disable_courseware_js': True,
        'display_course_modes_on_dashboard': enable_verified_certificates and display_course_modes_on_dashboard,
        'display_sidebar_on_dashboard': display_sidebar_on_dashboard,
        'display_sidebar_account_activation_message': not(user.is_active or hide_dashboard_courses_until_activated),
        'display_dashboard_courses': (user.is_active or not hide_dashboard_courses_until_activated),
        'empty_dashboard_message': empty_dashboard_message,
        'recovery_email_message': recovery_email_message,
        'recovery_email_activation_message': recovery_email_activation_message,
        'enterprise_learner_portal_enabled_message': enterprise_learner_portal_enabled_message,
        'show_load_all_courses_link': show_load_all_courses_link(user, course_limit, course_enrollments),
        # TODO START: clean up as part of REVEM-199 (START)
        'course_info': get_dashboard_course_info(user, course_enrollments),
        # TODO START: clean up as part of REVEM-199 (END)
        #added by dev 
        'courseslist':courseslist,
        'lectureslist':lectureslist,
    }

    context_from_plugins = get_plugins_view_context(
        ProjectType.LMS,
        COURSE_DASHBOARD_PLUGIN_VIEW_NAME,
        context
    )
    context.update(context_from_plugins)

    course = None
    context.update(
        get_experiment_user_metadata_context(
            course,
            user,
        )
    )
    if ecommerce_service.is_enabled(request.user):
        context.update({
            'use_ecommerce_payment_flow': True,
            'ecommerce_payment_page': ecommerce_service.payment_page_url(),
        })

    # Gather urls for course card resume buttons.
    resume_button_urls = ['' for entitlement in course_entitlements]
    for url in get_resume_urls_for_enrollments(user, course_enrollments).values():
        resume_button_urls.append(url)
    # There must be enough urls for dashboard.html. Template creates course
    # cards for "enrollments + entitlements".
    context.update({
        'resume_button_urls': resume_button_urls
    })

    return render_to_response('dashboard.html', context)