def assert_access_to_gated_content(self, user, expected_access):
        """
        Verifies access to gated content for the given user is as expected.
        """
        # clear the request cache to flush any cached access results
        RequestCache.clear_request_cache()

        # access to gating content (seq1) remains constant
        self.assertTrue(bool(has_access(user, 'load', self.seq1, self.course.id)))

        # access to gated content (seq2) is as expected
        self.assertEquals(bool(has_access(user, 'load', self.seq2, self.course.id)), expected_access)
Example #2
0
    def assert_access_to_gated_content(self, user):
        """
        Verifies access to gated content for the given user is as expected.
        """
        # clear the request cache to flush any cached access results
        RequestCache.clear_all_namespaces()

        # access to gating content (seq1) remains constant
        self.assertTrue(bool(has_access(user, 'load', self.seq1, self.course.id)))

        # access to gated content (seq2) remains constant, access is prevented in SeqModule loading
        self.assertTrue(bool(has_access(user, 'load', self.seq2, self.course.id)))
Example #3
0
    def _get_course(self, request, course_id):
        """
        Returns the course after parsing the id, checking access, and checking existence.
        """
        try:
            course_key = get_course_key(request, course_id)
        except InvalidKeyError:
            raise self.api_error(
                status_code=status.HTTP_400_BAD_REQUEST,
                developer_message='The provided course key cannot be parsed.',
                error_code='invalid_course_key'
            )

        if not has_access(request.user, 'staff', course_key):
            raise self.api_error(
                status_code=status.HTTP_403_FORBIDDEN,
                developer_message='The course does not exist.',
                error_code='user_or_course_does_not_exist',
            )

        course = modulestore().get_course(course_key, depth=0)
        if not course:
            raise self.api_error(
                status_code=status.HTTP_404_NOT_FOUND,
                developer_message='The course does not exist.',
                error_code='user_or_course_does_not_exist',
            )
        return course
Example #4
0
def get_course_calendar(user, course_key_string):
    try:
        from icalendar import Calendar, Event
    except ImportError:
        logging.error('Calendar module not installed')
        return

    course_key = CourseKey.from_string(course_key_string)
    checked = ['course', 'vertical', 'sequential']
    items = modulestore().get_items(course_key)
    hour = timedelta(hours=1)

    cal = Calendar()
    for num, item in enumerate(items):
        if not item.category in checked:
            continue
        if not item.graded:
            continue
        if not has_access(user, 'load', item, course_key=item.location.course_key):
            continue
        if not item.due:
            continue
        if item.category != 'course':
            format = item.format or item.get_parent().format
        else:
            format = 'course'
        url = u'http://{}{}'.format(settings.SITE_NAME, _reverse_usage(item))
        event = Event()
        summary = u'Type: {}; Name: {}({})'.format(format, item.display_name, url).encode('utf-8')
        event.add('summary', summary)
        event.add('dtstart', item.due - hour)
        event.add('dtend', item.due)
        cal.add_component(event)
    text = cal.to_ical().decode('utf-8')
    return text
Example #5
0
    def transform_block_filters(self, usage_info, block_structure):
        user = usage_info.user
        result_list = SplitTestTransformer().transform_block_filters(usage_info, block_structure)

        user_partitions = block_structure.get_transformer_data(self, 'user_partitions')
        if not user_partitions:
            return [block_structure.create_universal_filter()]

        user_groups = get_user_partition_groups(usage_info.course_key, user_partitions, user, 'id')

        for block_key in block_structure.topological_traversal():
            transformer_block_field = block_structure.get_transformer_block_field(
                block_key, self, 'merged_group_access'
            )
            access_denying_partition_id = transformer_block_field.get_access_denying_partition(
                user_groups
            )
            access_denying_partition = get_partition_from_id(user_partitions, access_denying_partition_id)

            if not has_access(user, 'staff', block_key) and access_denying_partition:
                user_group = user_groups.get(access_denying_partition.id)
                allowed_groups = transformer_block_field.get_allowed_groups()[access_denying_partition.id]
                access_denied_message = access_denying_partition.access_denied_message(
                    block_key, user, user_group, allowed_groups
                )
                block_structure.override_xblock_field(
                    block_key, 'authorization_denial_reason', access_denying_partition.name
                )
                block_structure.override_xblock_field(
                    block_key, 'authorization_denial_message', access_denied_message
                )

        group_access_filter = block_structure.create_removal_filter(
            lambda block_key: (
                not has_access(user, 'staff', block_key) and
                block_structure.get_transformer_block_field(
                    block_key, self, 'merged_group_access'
                ).get_access_denying_partition(user_groups) is not None and
                block_structure.get_xblock_field(block_key, 'authorization_denial_message') is None
            )
        )
        result_list.append(group_access_filter)
        return result_list
        def check_results(user, expected_accessible_blocks, blocks_with_differing_access):
            """
            Verifies the results of transforming the blocks in the
            course for the given user.
            """

            self.client.login(username=user.username, password=self.password)
            block_structure = get_course_blocks(user, self.course.location, transformers=transformers)

            # Enumerate through all the blocks that were created in the
            # course
            for i, xblock_key in enumerate(self.xblock_keys):

                # verify existence of the block
                block_structure_result = block_structure.has_block(xblock_key)
                has_access_result = bool(has_access(user, 'load', self.get_block(i), course_key=self.course.id))

                # compare with expected value
                self.assertEquals(
                    block_structure_result,
                    i in expected_accessible_blocks,
                    "block_structure return value {0} not equal to expected value for block {1} for user {2}".format(
                        block_structure_result, i, user.username
                    )
                )

                # compare with has_access result
                if i in blocks_with_differing_access:
                    self.assertNotEqual(
                        block_structure_result,
                        has_access_result,
                        "block structure ({0}) & has_access ({1}) results are equal for block {2} for user {3}".format(
                            block_structure_result, has_access_result, i, user.username
                        )
                    )
                else:
                    self.assertEquals(
                        block_structure_result,
                        has_access_result,
                        "block structure ({0}) & has_access ({1}) results not equal for block {2} for user {3}".format(
                            block_structure_result, has_access_result, i, user.username
                        )
                    )

            self.client.logout()
Example #7
0
    def transform_block_filters(self, usage_info, block_structure):
        user = usage_info.user
        result_list = SplitTestTransformer().transform_block_filters(usage_info, block_structure)

        user_partitions = block_structure.get_transformer_data(self, 'user_partitions')
        if not user_partitions:
            return [block_structure.create_universal_filter()]

        user_groups = get_user_partition_groups(usage_info.course_key, user_partitions, user, 'id')
        group_access_filter = block_structure.create_removal_filter(
            lambda block_key: not (
                has_access(user, 'staff', block_key) or
                block_structure.get_transformer_block_field(block_key, self, 'merged_group_access').check_group_access(
                    user_groups
                )
            )
        )

        result_list.append(group_access_filter)
        return result_list
Example #8
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))

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

    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)
Example #9
0
    def get(self, request, *args, **kwargs):
        course_key_string = kwargs.get('course_key_string')
        course_key = CourseKey.from_string(course_key_string)
        course_usage_key = modulestore().make_course_usage_key(course_key)

        # Enable NR tracing for this view based on course
        monitoring_utils.set_custom_metric('course_id', course_key_string)
        monitoring_utils.set_custom_metric('user_id', request.user.id)
        monitoring_utils.set_custom_metric('is_staff', request.user.is_staff)

        course = get_course_with_access(request.user,
                                        'load',
                                        course_key,
                                        check_if_enrolled=False)

        _, request.user = setup_masquerade(
            request,
            course_key,
            staff_access=has_access(request.user, 'staff', course_key),
            reset_masquerade_data=True,
        )

        course_tools = CourseToolsPluginManager.get_enabled_course_tools(
            request, course_key)
        date_blocks = get_course_date_blocks(course,
                                             request.user,
                                             request,
                                             num_assignments=1)

        # User locale settings
        user_timezone_locale = user_timezone_locale_prefs(request)
        user_timezone = user_timezone_locale['user_timezone']

        dates_tab_link = request.build_absolute_uri(
            reverse('dates', args=[course.id]))
        if course_home_mfe_dates_tab_is_active(course.id):
            dates_tab_link = get_microfrontend_url(course_key=course.id,
                                                   view_name='dates')

        transformers = BlockStructureTransformers()
        transformers += course_blocks_api.get_course_block_access_transformers(
            request.user)
        transformers += [
            BlocksAPITransformer(None, None, depth=3),
        ]

        course_blocks = get_course_blocks(request.user,
                                          course_usage_key,
                                          transformers,
                                          include_completion=True)

        dates_widget = {
            'course_date_blocks': [
                block for block in date_blocks
                if not isinstance(block, TodaysDate)
            ],
            'dates_tab_link':
            dates_tab_link,
            'user_timezone':
            user_timezone,
        }

        data = {
            'course_tools': course_tools,
            'course_blocks': course_blocks,
            'dates_widget': dates_widget,
        }
        context = self.get_serializer_context()
        context['course_key'] = course_key
        serializer = self.get_serializer_class()(data, context=context)

        return Response(serializer.data)
 def should_remove(self, user):
     """ Test to see if this result should be removed due to access restriction """
     if has_access(user, 'staff', self.get_course_key()):
         return False
     return self.get_usage_key() not in self.get_course_blocks(user).get_block_keys()
Example #11
0
    def get(self, request, *args, **kwargs):
        course_key_string = kwargs.get('course_key_string')
        course_key = CourseKey.from_string(course_key_string)
        course_usage_key = modulestore().make_course_usage_key(course_key)

        # Enable NR tracing for this view based on course
        monitoring_utils.set_custom_metric('course_id', course_key_string)
        monitoring_utils.set_custom_metric('user_id', request.user.id)
        monitoring_utils.set_custom_metric('is_staff', request.user.is_staff)

        course = get_course_with_access(request.user,
                                        'load',
                                        course_key,
                                        check_if_enrolled=False)

        _, request.user = setup_masquerade(
            request,
            course_key,
            staff_access=has_access(request.user, 'staff', course_key),
            reset_masquerade_data=True,
        )

        enrollment = CourseEnrollment.get_enrollment(request.user, course_key)
        allow_anonymous = COURSE_ENABLE_UNENROLLED_ACCESS_FLAG.is_enabled(
            course_key)
        allow_public = allow_anonymous and course.course_visibility == COURSE_VISIBILITY_PUBLIC
        is_enrolled = enrollment and enrollment.is_active
        is_staff = has_access(request.user, 'staff', course_key)

        show_handouts = is_enrolled or is_staff or allow_public
        handouts_html = get_course_info_section(
            request, request.user, course, 'handouts') if show_handouts else ''

        course_tools = CourseToolsPluginManager.get_enabled_course_tools(
            request, course_key)
        date_blocks = get_course_date_blocks(course,
                                             request.user,
                                             request,
                                             num_assignments=1)

        # User locale settings
        user_timezone_locale = user_timezone_locale_prefs(request)
        user_timezone = user_timezone_locale['user_timezone']

        dates_tab_link = request.build_absolute_uri(
            reverse('dates', args=[course.id]))
        if course_home_mfe_dates_tab_is_active(course.id):
            dates_tab_link = get_microfrontend_url(course_key=course.id,
                                                   view_name='dates')

        transformers = BlockStructureTransformers()
        transformers += get_course_block_access_transformers(request.user)
        transformers += [
            BlocksAPITransformer(None, None, depth=3),
        ]

        course_blocks = get_course_blocks(request.user,
                                          course_usage_key,
                                          transformers,
                                          include_completion=True)

        dates_widget = {
            'course_date_blocks': [
                block for block in date_blocks
                if not isinstance(block, TodaysDate)
            ],
            'dates_tab_link':
            dates_tab_link,
            'user_timezone':
            user_timezone,
        }

        data = {
            'course_blocks': course_blocks,
            'course_tools': course_tools,
            'dates_widget': dates_widget,
            'handouts_html': handouts_html,
        }
        context = self.get_serializer_context()
        context['course_key'] = course_key
        serializer = self.get_serializer_class()(data, context=context)

        return Response(serializer.data)
Example #12
0
 def test__user_passed_as_none(self):
     """Ensure has_access handles a user being passed as null"""
     access.has_access(None, 'staff', 'global', None)
Example #13
0
    def get(self, request, *args, **kwargs):
        course_key_string = kwargs.get('course_key_string')
        course_key = CourseKey.from_string(course_key_string)
        course_usage_key = modulestore().make_course_usage_key(course_key)

        if not course_home_mfe_outline_tab_is_active(course_key):
            raise Http404

        # Enable NR tracing for this view based on course
        monitoring_utils.set_custom_metric('course_id', course_key_string)
        monitoring_utils.set_custom_metric('user_id', request.user.id)
        monitoring_utils.set_custom_metric('is_staff', request.user.is_staff)

        course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=False)

        _masquerade, request.user = setup_masquerade(
            request,
            course_key,
            staff_access=has_access(request.user, 'staff', course_key),
            reset_masquerade_data=True,
        )

        course_overview = CourseOverview.get_from_id(course_key)
        enrollment = CourseEnrollment.get_enrollment(request.user, course_key)
        allow_anonymous = COURSE_ENABLE_UNENROLLED_ACCESS_FLAG.is_enabled(course_key)
        allow_public = allow_anonymous and course.course_visibility == COURSE_VISIBILITY_PUBLIC
        is_enrolled = enrollment and enrollment.is_active
        is_staff = has_access(request.user, 'staff', course_key)
        show_enrolled = is_enrolled or is_staff

        show_handouts = show_enrolled or allow_public
        handouts_html = get_course_info_section(request, request.user, course, 'handouts') if show_handouts else ''

        # TODO: TNL-7185 Legacy: Refactor to return the offer & expired data and format the message in the MFE
        offer_html = generate_offer_html(request.user, course_overview)
        course_expired_html = generate_course_expired_message(request.user, course_overview)

        welcome_message_html = None
        if get_course_tag(request.user, course_key, PREFERENCE_KEY) != 'False':
            if LATEST_UPDATE_FLAG.is_enabled(course_key):
                welcome_message_html = LatestUpdateFragmentView().latest_update_html(request, course)
            else:
                welcome_message_html = WelcomeMessageFragmentView().welcome_message_html(request, course)

        enroll_alert = {
            'can_enroll': True,
            'extra_text': None,
        }
        if not show_enrolled:
            if CourseMode.is_masters_only(course_key):
                enroll_alert['can_enroll'] = False
                enroll_alert['extra_text'] = _('Please contact your degree administrator or '
                                               'edX Support if you have questions.')
            elif course.invitation_only:
                enroll_alert['can_enroll'] = False

        course_tools = CourseToolsPluginManager.get_enabled_course_tools(request, course_key)
        date_blocks = get_course_date_blocks(course, request.user, request, num_assignments=1)

        # User locale settings
        user_timezone_locale = user_timezone_locale_prefs(request)
        user_timezone = user_timezone_locale['user_timezone']

        dates_tab_link = request.build_absolute_uri(reverse('dates', args=[course.id]))
        if course_home_mfe_dates_tab_is_active(course.id):
            dates_tab_link = get_microfrontend_url(course_key=course.id, view_name='dates')

        transformers = BlockStructureTransformers()
        transformers += get_course_block_access_transformers(request.user)
        transformers += [
            BlocksAPITransformer(None, None, depth=4),
        ]

        course_blocks = get_course_blocks(request.user, course_usage_key, transformers, include_completion=True)

        has_visited_course = False
        try:
            resume_block = get_key_to_last_completed_block(request.user, course.id)
            has_visited_course = True
        except UnavailableCompletionData:
            resume_block = course_usage_key

        resume_path = reverse('jump_to', kwargs={
            'course_id': course_key_string,
            'location': str(resume_block)
        })
        resume_course_url = request.build_absolute_uri(resume_path)

        resume_course = {
            'has_visited_course': has_visited_course,
            'url': resume_course_url,
        }

        dates_widget = {
            'course_date_blocks': [block for block in date_blocks if not isinstance(block, TodaysDate)],
            'dates_tab_link': dates_tab_link,
            'user_timezone': user_timezone,
        }

        data = {
            'course_blocks': course_blocks,
            'course_expired_html': course_expired_html,
            'course_tools': course_tools,
            'dates_widget': dates_widget,
            'enroll_alert': enroll_alert,
            'handouts_html': handouts_html,
            'offer_html': offer_html,
            'resume_course': resume_course,
            'welcome_message_html': welcome_message_html,
        }
        context = self.get_serializer_context()
        context['course_key'] = course_key
        serializer = self.get_serializer_class()(data, context=context)

        return Response(serializer.data)
Example #14
0
 def ensure_staff_access(self, block_location):
     """
     Another DRY helper.
     """
     block = modulestore().get_item(block_location)
     assert access.has_access(self.staff, 'load', block, self.course.id)
Example #15
0
 def check_access(self, user, block_location, is_accessible):
     """
     DRY helper.
     """
     assert bool(access.has_access(user, 'load', modulestore().get_item(block_location), self.course.id))\
            is is_accessible
Example #16
0
    def get(self, request, *args, **kwargs):
        course_key_string = kwargs.get('course_key_string')
        course_key = CourseKey.from_string(course_key_string)
        course_usage_key = modulestore().make_course_usage_key(course_key)

        if not course_home_mfe_outline_tab_is_active(course_key):
            raise Http404

        # Enable NR tracing for this view based on course
        monitoring_utils.set_custom_attribute('course_id', course_key_string)
        monitoring_utils.set_custom_attribute('user_id', request.user.id)
        monitoring_utils.set_custom_attribute('is_staff',
                                              request.user.is_staff)

        course = get_course_with_access(request.user,
                                        'load',
                                        course_key,
                                        check_if_enrolled=False)

        _masquerade, request.user = setup_masquerade(
            request,
            course_key,
            staff_access=has_access(request.user, 'staff', course_key),
            reset_masquerade_data=True,
        )

        course_overview = CourseOverview.get_from_id(course_key)
        enrollment = CourseEnrollment.get_enrollment(request.user, course_key)
        allow_anonymous = COURSE_ENABLE_UNENROLLED_ACCESS_FLAG.is_enabled(
            course_key)
        allow_public = allow_anonymous and course.course_visibility == COURSE_VISIBILITY_PUBLIC
        allow_public_outline = allow_anonymous and course.course_visibility == COURSE_VISIBILITY_PUBLIC_OUTLINE

        # User locale settings
        user_timezone_locale = user_timezone_locale_prefs(request)
        user_timezone = user_timezone_locale['user_timezone']

        dates_tab_link = request.build_absolute_uri(
            reverse('dates', args=[course.id]))
        if course_home_mfe_dates_tab_is_active(course.id):
            dates_tab_link = get_microfrontend_url(course_key=course.id,
                                                   view_name='dates')

        # Set all of the defaults
        access_expiration = None
        course_blocks = None
        course_goals = {'goal_options': [], 'selected_goal': None}
        course_tools = CourseToolsPluginManager.get_enabled_course_tools(
            request, course_key)
        dates_widget = {
            'course_date_blocks': [],
            'dates_tab_link': dates_tab_link,
            'user_timezone': user_timezone,
        }
        enroll_alert = {
            'can_enroll': True,
            'extra_text': None,
        }
        handouts_html = None
        offer_data = None
        resume_course = {
            'has_visited_course': False,
            'url': None,
        }
        welcome_message_html = None

        is_enrolled = enrollment and enrollment.is_active
        is_staff = bool(has_access(request.user, 'staff', course_key))
        show_enrolled = is_enrolled or is_staff
        if show_enrolled:
            course_blocks = get_course_outline_block_tree(
                request, course_key_string, request.user)
            date_blocks = get_course_date_blocks(course,
                                                 request.user,
                                                 request,
                                                 num_assignments=1)
            dates_widget['course_date_blocks'] = [
                block for block in date_blocks
                if not isinstance(block, TodaysDate)
            ]

            handouts_html = get_course_info_section(request, request.user,
                                                    course, 'handouts')
            welcome_message_html = get_current_update_for_user(request, course)

            offer_data = generate_offer_data(request.user, course_overview)
            access_expiration = get_access_expiration_data(
                request.user, course_overview)

            # Only show the set course goal message for enrolled, unverified
            # users in a course that allows for verified statuses.
            is_already_verified = CourseEnrollment.is_enrolled_as_verified(
                request.user, course_key)
            if not is_already_verified and has_course_goal_permission(
                    request, course_key_string, {'is_enrolled': is_enrolled}):
                course_goals = {
                    'goal_options':
                    valid_course_goals_ordered(include_unsure=True),
                    'selected_goal': None
                }

                selected_goal = get_course_goal(request.user, course_key)
                if selected_goal:
                    course_goals['selected_goal'] = {
                        'key': selected_goal.goal_key,
                        'text': get_course_goal_text(selected_goal.goal_key),
                    }

            try:
                resume_block = get_key_to_last_completed_block(
                    request.user, course.id)
                resume_course['has_visited_course'] = True
                resume_path = reverse('jump_to',
                                      kwargs={
                                          'course_id': course_key_string,
                                          'location': str(resume_block)
                                      })
                resume_course['url'] = request.build_absolute_uri(resume_path)
            except UnavailableCompletionData:
                start_block = get_start_block(course_blocks)
                resume_course['url'] = start_block['lms_web_url']

        elif allow_public_outline or allow_public:
            course_blocks = get_course_outline_block_tree(
                request, course_key_string, None)
            if allow_public:
                handouts_html = get_course_info_section(
                    request, request.user, course, 'handouts')

        if not show_enrolled:
            if CourseMode.is_masters_only(course_key):
                enroll_alert['can_enroll'] = False
                enroll_alert['extra_text'] = _(
                    'Please contact your degree administrator or '
                    'edX Support if you have questions.')
            elif course.invitation_only:
                enroll_alert['can_enroll'] = False

        data = {
            'access_expiration': access_expiration,
            'course_blocks': course_blocks,
            'course_goals': course_goals,
            'course_tools': course_tools,
            'dates_widget': dates_widget,
            'enroll_alert': enroll_alert,
            'handouts_html': handouts_html,
            'has_ended': course.has_ended(),
            'offer': offer_data,
            'resume_course': resume_course,
            'welcome_message_html': welcome_message_html,
        }
        context = self.get_serializer_context()
        context['course_overview'] = course_overview
        context['enable_links'] = show_enrolled or allow_public
        context['enrollment'] = enrollment
        serializer = self.get_serializer_class()(data, context=context)

        return Response(serializer.data)
Example #17
0
    def get(self, request, *args, **kwargs):
        course_key_string = kwargs.get('course_key_string')
        course_key = CourseKey.from_string(course_key_string)
        student_id = kwargs.get('student_id')

        if not course_home_mfe_progress_tab_is_active(course_key):
            raise Http404

        # Enable NR tracing for this view based on course
        monitoring_utils.set_custom_attribute('course_id', course_key_string)
        monitoring_utils.set_custom_attribute('user_id', request.user.id)
        monitoring_utils.set_custom_attribute('is_staff',
                                              request.user.is_staff)
        is_staff = bool(has_access(request.user, 'staff', course_key))

        student = self._get_student_user(request, course_key, student_id,
                                         is_staff)
        username = get_enterprise_learner_generic_name(
            request) or student.username

        course = get_course_with_access(student,
                                        'load',
                                        course_key,
                                        check_if_enrolled=False)

        course_overview = CourseOverview.get_from_id(course_key)
        enrollment = CourseEnrollment.get_enrollment(student, course_key)
        enrollment_mode = getattr(enrollment, 'mode', None)

        if not (enrollment and enrollment.is_active) and not is_staff:
            return Response('User not enrolled.', status=401)

        # The block structure is used for both the course_grade and has_scheduled content fields
        # So it is called upfront and reused for optimization purposes
        collected_block_structure = get_block_structure_manager(
            course_key).get_collected()
        course_grade = CourseGradeFactory().read(
            student, collected_block_structure=collected_block_structure)

        # Get has_scheduled_content data
        transformers = BlockStructureTransformers()
        transformers += [
            start_date.StartDateTransformer(),
            ContentTypeGateTransformer()
        ]
        usage_key = collected_block_structure.root_block_usage_key
        course_blocks = get_course_blocks(
            student,
            usage_key,
            transformers=transformers,
            collected_block_structure=collected_block_structure,
            include_has_scheduled_content=True)
        has_scheduled_content = course_blocks.get_xblock_field(
            usage_key, 'has_scheduled_content')

        # Get user_has_passing_grade data
        user_has_passing_grade = False
        if not student.is_anonymous:
            user_grade = course_grade.percent
            user_has_passing_grade = user_grade >= course.lowest_passing_grade

        descriptor = modulestore().get_course(course_key)
        grading_policy = descriptor.grading_policy
        verification_status = IDVerificationService.user_status(student)
        verification_link = None
        if verification_status['status'] is None or verification_status[
                'status'] == 'expired':
            verification_link = IDVerificationService.get_verify_location(
                course_id=course_key)
        elif verification_status['status'] == 'must_reverify':
            verification_link = IDVerificationService.get_verify_location(
                course_id=course_key)
        verification_data = {
            'link': verification_link,
            'status': verification_status['status'],
            'status_date': verification_status['status_date'],
        }

        access_expiration = get_access_expiration_data(request.user,
                                                       course_overview)

        data = {
            'access_expiration':
            access_expiration,
            'certificate_data':
            get_cert_data(student, course, enrollment_mode, course_grade),
            'completion_summary':
            get_course_blocks_completion_summary(course_key, student),
            'course_grade':
            course_grade,
            'credit_course_requirements':
            credit_course_requirements(course_key, student),
            'end':
            course.end,
            'enrollment_mode':
            enrollment_mode,
            'grading_policy':
            grading_policy,
            'has_scheduled_content':
            has_scheduled_content,
            'section_scores':
            list(course_grade.chapter_grades.values()),
            'studio_url':
            get_studio_url(course, 'settings/grading'),
            'username':
            username,
            'user_has_passing_grade':
            user_has_passing_grade,
            'verification_data':
            verification_data,
        }
        context = self.get_serializer_context()
        context['staff_access'] = is_staff
        context['course_blocks'] = course_blocks
        context['course_key'] = course_key
        # course_overview and enrollment will be used by VerifiedModeSerializer
        context['course_overview'] = course_overview
        context['enrollment'] = enrollment
        serializer = self.get_serializer_class()(data, context=context)

        return Response(serializer.data)
Example #18
0
 def is_enabled(cls, course, user=None):
     return user and user.is_authenticated and \
         bool(CourseEnrollment.is_enrolled(user, course.id) or has_access(user, 'staff', course, course.id))
Example #19
0
 def is_enabled(cls, course, user=None):
     """
     Returns true if the specified user has staff access.
     """
     return bool(user and has_access(user, 'staff', course, course.id))
Example #20
0
    def test_has_access_in_preview_mode_with_group(self):
        """
        Test that a user masquerading as a member of a group sees appropriate content in preview mode.
        """
        # Note about UserPartition and UserPartition Group IDs: these must not conflict with IDs used
        # by dynamic user partitions.
        partition_id = MINIMUM_STATIC_PARTITION_ID
        group_0_id = MINIMUM_STATIC_PARTITION_ID + 1
        group_1_id = MINIMUM_STATIC_PARTITION_ID + 2
        user_partition = UserPartition(
            partition_id,
            'Test User Partition',
            '', [Group(group_0_id, 'Group 1'),
                 Group(group_1_id, 'Group 2')],
            scheme_id='cohort')
        self.course.user_partitions.append(user_partition)
        self.course.cohort_config = {'cohorted': True}

        chapter = ItemFactory.create(category="chapter",
                                     parent_location=self.course.location)
        chapter.group_access = {partition_id: [group_0_id]}

        modulestore().update_item(self.course, ModuleStoreEnum.UserID.test)

        # User should not be able to preview when masquerading as student (and not in the group above).
        with patch('lms.djangoapps.courseware.access.get_user_role'
                   ) as mock_user_role:
            mock_user_role.return_value = 'student'
            assert not bool(
                access.has_access(self.global_staff,
                                  'load',
                                  chapter,
                                  course_key=self.course.id))

        # Should be able to preview when in staff or instructor role.
        for mocked_role in ['staff', 'instructor']:
            with patch('lms.djangoapps.courseware.access.get_user_role'
                       ) as mock_user_role:
                mock_user_role.return_value = mocked_role
                assert bool(
                    access.has_access(self.global_staff,
                                      'load',
                                      chapter,
                                      course_key=self.course.id))

        # Now install masquerade group and set staff as a member of that.
        assert 200 == masquerade_as_group_member(self.global_staff,
                                                 self.course, partition_id,
                                                 group_0_id)
        # Can load the chapter since user is in the group.
        assert bool(
            access.has_access(self.global_staff,
                              'load',
                              chapter,
                              course_key=self.course.id))

        # Move the user to be a part of the second group.
        assert 200 == masquerade_as_group_member(self.global_staff,
                                                 self.course, partition_id,
                                                 group_1_id)
        # Cannot load the chapter since user is in a different group.
        assert not bool(
            access.has_access(
                self.global_staff, 'load', chapter, course_key=self.course.id))
Example #21
0
    def render_to_fragment(self, request, course_id=None, **kwargs):
        """
        Renders the course's home page as a fragment.
        """
        course_key = CourseKey.from_string(course_id)
        course = get_course_with_access(request.user, 'load', course_key)

        # Render the course dates as a fragment
        dates_fragment = CourseDatesFragmentView().render_to_fragment(
            request, course_id=course_id, **kwargs)

        # Render the full content to enrolled users, as well as to course and global staff.
        # Unenrolled users who are not course or global staff are given only a subset.
        enrollment = CourseEnrollment.get_enrollment(request.user, course_key)
        user_access = {
            'is_anonymous': request.user.is_anonymous,
            'is_enrolled': enrollment and enrollment.is_active,
            'is_staff': has_access(request.user, 'staff', course_key),
        }

        allow_anonymous = COURSE_ENABLE_UNENROLLED_ACCESS_FLAG.is_enabled(
            course_key)
        allow_public = allow_anonymous and course.course_visibility == COURSE_VISIBILITY_PUBLIC
        allow_public_outline = allow_anonymous and course.course_visibility == COURSE_VISIBILITY_PUBLIC_OUTLINE

        # Set all the fragments
        outline_fragment = None
        update_message_fragment = None
        course_sock_fragment = None
        offer_banner_fragment = None
        course_expiration_fragment = None
        has_visited_course = None
        resume_course_url = None
        handouts_html = None

        if user_access['is_enrolled'] or user_access['is_staff']:
            outline_fragment = CourseOutlineFragmentView().render_to_fragment(
                request, course_id=course_id, **kwargs)
            if LATEST_UPDATE_FLAG.is_enabled(course_key):
                update_message_fragment = LatestUpdateFragmentView(
                ).render_to_fragment(request, course_id=course_id, **kwargs)
            else:
                update_message_fragment = WelcomeMessageFragmentView(
                ).render_to_fragment(request, course_id=course_id, **kwargs)
            course_sock_fragment = CourseSockFragmentView().render_to_fragment(
                request, course=course, **kwargs)
            has_visited_course, resume_course_url = self._get_resume_course_info(
                request, course_id)
            handouts_html = self._get_course_handouts(request, course)
            course_overview = CourseOverview.get_from_id(course.id)
            offer_banner_fragment = get_first_purchase_offer_banner_fragment(
                request.user, course_overview)
            course_expiration_fragment = generate_course_expired_fragment(
                request.user, course_overview)
        elif allow_public_outline or allow_public:
            outline_fragment = CourseOutlineFragmentView().render_to_fragment(
                request, course_id=course_id, user_is_enrolled=False, **kwargs)
            course_sock_fragment = CourseSockFragmentView().render_to_fragment(
                request, course=course, **kwargs)
            if allow_public:
                handouts_html = self._get_course_handouts(request, course)
        else:
            # Redirect the user to the dashboard if they are not enrolled and
            # this is a course that does not support direct enrollment.
            if not can_self_enroll_in_course(course_key):
                raise CourseAccessRedirect(reverse('dashboard'))

        # Get the course tools enabled for this user and course
        course_tools = CourseToolsPluginManager.get_enabled_course_tools(
            request, course_key)

        # Check if the user can access the course goal functionality
        has_goal_permission = has_course_goal_permission(
            request, course_id, user_access)

        # Grab the current course goal and the acceptable course goal keys mapped to translated values
        current_goal = get_course_goal(request.user, course_key)
        goal_options = get_course_goal_options()

        # Get the course goals api endpoint
        goal_api_url = get_goal_api_url(request)

        # Grab the course home messages fragment to render any relevant django messages
        course_home_message_fragment = CourseHomeMessageFragmentView(
        ).render_to_fragment(request,
                             course_id=course_id,
                             user_access=user_access,
                             **kwargs)

        # Get info for upgrade messaging
        upgrade_price = None
        upgrade_url = None
        has_discount = False

        # TODO Add switch to control deployment
        if SHOW_UPGRADE_MSG_ON_COURSE_HOME.is_enabled(
                course_key) and enrollment and enrollment.upgrade_deadline:
            upgrade_url = EcommerceService().upgrade_url(
                request.user, course_key)
            upgrade_price, has_discount = format_strikeout_price(
                request.user, course)

        # Render the course home fragment
        context = {
            'request': request,
            'csrf': csrf(request)['csrf_token'],
            'course': course,
            'course_key': course_key,
            'outline_fragment': outline_fragment,
            'handouts_html': handouts_html,
            'course_home_message_fragment': course_home_message_fragment,
            'offer_banner_fragment': offer_banner_fragment,
            'course_expiration_fragment': course_expiration_fragment,
            'has_visited_course': has_visited_course,
            'resume_course_url': resume_course_url,
            'course_tools': course_tools,
            'dates_fragment': dates_fragment,
            'username': request.user.username,
            'goal_api_url': goal_api_url,
            'has_goal_permission': has_goal_permission,
            'goal_options': goal_options,
            'current_goal': current_goal,
            'update_message_fragment': update_message_fragment,
            'course_sock_fragment': course_sock_fragment,
            'disable_courseware_js': True,
            'uses_pattern_library': True,
            'upgrade_price': upgrade_price,
            'upgrade_url': upgrade_url,
            'has_discount': has_discount,
        }
        html = render_to_string('course_experience/course-home-fragment.html',
                                context)
        return Fragment(html)
Example #22
0
    def get(self, request, *args, **kwargs):
        course_key_string = kwargs.get('course_key_string')
        course_key = CourseKey.from_string(course_key_string)

        # Enable NR tracing for this view based on course
        monitoring_utils.set_custom_metric('course_id', course_key_string)
        monitoring_utils.set_custom_metric('user_id', request.user.id)
        monitoring_utils.set_custom_metric('is_staff', request.user.is_staff)

        _, request.user = setup_masquerade(
            request,
            course_key,
            staff_access=has_access(request.user, 'staff', course_key),
            reset_masquerade_data=True
        )

        user_timezone_locale = user_timezone_locale_prefs(request)
        user_timezone = user_timezone_locale['user_timezone']

        transformers = BlockStructureTransformers()
        transformers += course_blocks_api.get_course_block_access_transformers(request.user)
        transformers += [
            BlocksAPITransformer(None, None, depth=3),
        ]
        course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)

        enrollment_mode, _ = CourseEnrollment.enrollment_mode_for_user(request.user, course_key)

        course_grade = CourseGradeFactory().read(request.user, course)
        courseware_summary = course_grade.chapter_grades.values()

        verification_status = IDVerificationService.user_status(request.user)
        verification_link = None
        if verification_status['status'] is None or verification_status['status'] == 'expired':
            verification_link = IDVerificationService.get_verify_location('verify_student_verify_now',
                                                                          course_id=course_key)
        elif verification_status['status'] == 'must_reverify':
            verification_link = IDVerificationService.get_verify_location('verify_student_reverify',
                                                                          course_id=course_key)
        verification_data = {
            'link': verification_link,
            'status': verification_status['status'],
            'status_date': verification_status['status_date'],
        }

        data = {
            'certificate_data': get_cert_data(request.user, course, enrollment_mode, course_grade),
            'courseware_summary': courseware_summary,
            'credit_course_requirements': credit_course_requirements(course_key, request.user),
            'credit_support_url': CREDIT_SUPPORT_URL,
            'enrollment_mode': enrollment_mode,
            'studio_url': get_studio_url(course, 'settings/grading'),
            'user_timezone': user_timezone,
            'verification_data': verification_data,
        }
        context = self.get_serializer_context()
        context['staff_access'] = bool(has_access(request.user, 'staff', course))
        context['course_key'] = course_key
        serializer = self.get_serializer_class()(data, context=context)

        return Response(serializer.data)
Example #23
0
 def _attach_course_run_can_enroll(self, run_mode):
     run_mode['can_enroll'] = bool(
         has_access(self.user, 'enroll', self.course_overview))
def instructor_dashboard_2(request, course_id):
    """ Display the instructor dashboard for a course. """
    try:
        course_key = CourseKey.from_string(course_id)
    except InvalidKeyError:
        log.error(
            u"Unable to find course with course key %s while loading the Instructor Dashboard.",
            course_id)
        return HttpResponseServerError()

    course = get_course_by_id(course_key, depth=0)

    access = {
        'admin':
        request.user.is_staff,
        'instructor':
        bool(has_access(request.user, 'instructor', course)),
        'finance_admin':
        CourseFinanceAdminRole(course_key).has_user(request.user),
        'sales_admin':
        CourseSalesAdminRole(course_key).has_user(request.user),
        'staff':
        bool(has_access(request.user, 'staff', course)),
        'forum_admin':
        has_forum_access(request.user, course_key, FORUM_ROLE_ADMINISTRATOR),
        'data_researcher':
        request.user.has_perm(permissions.CAN_RESEARCH, course_key),
    }

    if not request.user.has_perm(permissions.VIEW_DASHBOARD, course_key):
        raise Http404()

    is_white_label = CourseMode.is_white_label(course_key)

    reports_enabled = configuration_helpers.get_value('SHOW_ECOMMERCE_REPORTS',
                                                      False)

    sections = []
    if access['staff']:
        sections.extend([
            _section_course_info(course, access),
            _section_membership(course, access),
            _section_cohort_management(course, access),
            _section_discussions_management(course, access),
            _section_student_admin(course, access),
        ])
    if access['data_researcher']:
        sections.append(_section_data_download(course, access))

    analytics_dashboard_message = None
    if show_analytics_dashboard_message(course_key) and (access['staff'] or
                                                         access['instructor']):
        # Construct a URL to the external analytics dashboard
        analytics_dashboard_url = '{0}/courses/{1}'.format(
            settings.ANALYTICS_DASHBOARD_URL, six.text_type(course_key))
        link_start = HTML(u"<a href=\"{}\" rel=\"noopener\" target=\"_blank\">"
                          ).format(analytics_dashboard_url)
        analytics_dashboard_message = _(
            u"To gain insights into student enrollment and participation {link_start}"
            u"visit {analytics_dashboard_name}, our new course analytics product{link_end}."
        )
        analytics_dashboard_message = Text(analytics_dashboard_message).format(
            link_start=link_start,
            link_end=HTML("</a>"),
            analytics_dashboard_name=settings.ANALYTICS_DASHBOARD_NAME)

        # Temporarily show the "Analytics" section until we have a better way of linking to Insights
        sections.append(_section_analytics(course, access))

    # Check if there is corresponding entry in the CourseMode Table related to the Instructor Dashboard course
    course_mode_has_price = False
    paid_modes = CourseMode.paid_modes_for_course(course_key)
    if len(paid_modes) == 1:
        course_mode_has_price = True
    elif len(paid_modes) > 1:
        log.error(
            u"Course %s has %s course modes with payment options. Course must only have "
            u"one paid course mode to enable eCommerce options.",
            six.text_type(course_key), len(paid_modes))

    if access['instructor'] and is_enabled_for_course(course_key):
        sections.insert(3, _section_extensions(course))

    # Gate access to course email by feature flag & by course-specific authorization
    if is_bulk_email_feature_enabled(course_key) and (access['staff']
                                                      or access['instructor']):
        sections.append(_section_send_email(course, access))

    # Gate access to Special Exam tab depending if either timed exams or proctored exams
    # are enabled in the course

    user_has_access = any([
        request.user.is_staff,
        CourseStaffRole(course_key).has_user(request.user),
        CourseInstructorRole(course_key).has_user(request.user)
    ])
    course_has_special_exams = course.enable_proctored_exams or course.enable_timed_exams
    can_see_special_exams = course_has_special_exams and user_has_access and settings.FEATURES.get(
        'ENABLE_SPECIAL_EXAMS', False)

    if can_see_special_exams:
        sections.append(_section_special_exams(course, access))
    # Certificates panel
    # This is used to generate example certificates
    # and enable self-generated certificates for a course.
    # Note: This is hidden for all CCXs
    certs_enabled = CertificateGenerationConfiguration.current(
    ).enabled and not hasattr(course_key, 'ccx')
    if certs_enabled and access['admin']:
        sections.append(_section_certificates(course))

    openassessment_blocks = modulestore().get_items(
        course_key, qualifiers={'category': 'openassessment'})
    # filter out orphaned openassessment blocks
    openassessment_blocks = [
        block for block in openassessment_blocks if block.parent is not None
    ]
    if len(openassessment_blocks) > 0 and access['staff']:
        sections.append(
            _section_open_response_assessment(request, course,
                                              openassessment_blocks, access))

    disable_buttons = not CourseEnrollment.objects.is_small_course(course_key)

    certificate_white_list = CertificateWhitelist.get_certificate_white_list(
        course_key)
    generate_certificate_exceptions_url = reverse(
        'generate_certificate_exceptions',
        kwargs={
            'course_id': six.text_type(course_key),
            'generate_for': ''
        })
    generate_bulk_certificate_exceptions_url = reverse(
        'generate_bulk_certificate_exceptions',
        kwargs={'course_id': six.text_type(course_key)})
    certificate_exception_view_url = reverse(
        'certificate_exception_view',
        kwargs={'course_id': six.text_type(course_key)})

    certificate_invalidation_view_url = reverse(
        'certificate_invalidation_view',
        kwargs={'course_id': six.text_type(course_key)})

    certificate_invalidations = CertificateInvalidation.get_certificate_invalidations(
        course_key)

    context = {
        'course':
        course,
        'studio_url':
        get_studio_url(course, 'course'),
        'sections':
        sections,
        'disable_buttons':
        disable_buttons,
        'analytics_dashboard_message':
        analytics_dashboard_message,
        'certificate_white_list':
        certificate_white_list,
        'certificate_invalidations':
        certificate_invalidations,
        'generate_certificate_exceptions_url':
        generate_certificate_exceptions_url,
        'generate_bulk_certificate_exceptions_url':
        generate_bulk_certificate_exceptions_url,
        'certificate_exception_view_url':
        certificate_exception_view_url,
        'certificate_invalidation_view_url':
        certificate_invalidation_view_url,
        'xqa_server':
        settings.FEATURES.get('XQA_SERVER', "http://your_xqa_server.com"),
    }

    return render_to_response(
        'instructor/instructor_dashboard_2/instructor_dashboard_2.html',
        context)
Example #25
0
def can_access_others_blocks(requesting_user, course_key):
    """
    Returns whether the requesting_user can access the blocks for
    other users in the given course.
    """
    return has_access(requesting_user, CourseStaffRole.ROLE, course_key)
Example #26
0
def check_course_access(course,
                        user,
                        action,
                        check_if_enrolled=False,
                        check_survey_complete=True):
    """
    Check that the user has the access to perform the specified action
    on the course (CourseDescriptor|CourseOverview).

    check_if_enrolled: If true, additionally verifies that the user is enrolled.
    check_survey_complete: If true, additionally verifies that the user has completed the survey.
    """
    # Allow staff full access to the course even if not enrolled
    if has_access(user, 'staff', course.id):
        return

    request = get_current_request()
    check_content_start_date_for_masquerade_user(course.id, user, request,
                                                 course.start)

    access_response = has_access(user, action, course, course.id)
    if not access_response:
        # Redirect if StartDateError
        if isinstance(access_response, StartDateError):
            start_date = strftime_localized(course.start, 'SHORT_DATE')
            params = QueryDict(mutable=True)
            params['notlive'] = start_date
            raise CourseAccessRedirect(
                '{dashboard_url}?{params}'.format(
                    dashboard_url=reverse('dashboard'),
                    params=params.urlencode()), access_response)

        # Redirect if AuditExpiredError
        if isinstance(access_response, AuditExpiredError):
            params = QueryDict(mutable=True)
            params[
                'access_response_error'] = access_response.additional_context_user_message
            raise CourseAccessRedirect(
                '{dashboard_url}?{params}'.format(
                    dashboard_url=reverse('dashboard'),
                    params=params.urlencode()), access_response)

        # Redirect if the user must answer a survey before entering the course.
        if isinstance(access_response, MilestoneAccessError):
            raise CourseAccessRedirect(
                '{dashboard_url}'.format(dashboard_url=reverse('dashboard'), ),
                access_response)

        # Deliberately return a non-specific error message to avoid
        # leaking info about access control settings
        raise CoursewareAccessException(access_response)

    if check_if_enrolled:
        # If the user is not enrolled, redirect them to the about page
        if not CourseEnrollment.is_enrolled(user, course.id):
            raise CourseAccessRedirect(
                reverse('about_course', args=[six.text_type(course.id)]))

    # Redirect if the user must answer a survey before entering the course.
    if check_survey_complete and action == 'load':
        if is_survey_required_and_unanswered(user, course):
            raise CourseAccessRedirect(
                reverse('course_survey', args=[six.text_type(course.id)]))
Example #27
0
    def to_representation(self, course_overview):
        course_id = six.text_type(course_overview.id)
        request = self.context.get('request')
        api_version = self.context.get('api_version')

        return {
            # identifiers
            'id':
            course_id,
            'name':
            course_overview.display_name,
            'number':
            course_overview.display_number_with_default,
            'org':
            course_overview.display_org_with_default,

            # dates
            'start':
            course_overview.start,
            'start_display':
            course_overview.start_display,
            'start_type':
            course_overview.start_type,
            'end':
            course_overview.end,

            # notification info
            'subscription_id':
            course_overview.clean_id(padding_char='_'),

            # access info
            'courseware_access':
            has_access(request.user, 'load_mobile', course_overview).to_json(),

            # various URLs
            # course_image is sent in both new and old formats
            # (within media to be compatible with the new Course API)
            'media': {
                'course_image': {
                    'uri': course_overview.course_image_url,
                    'name': 'Course Image',
                }
            },
            'course_image':
            course_overview.course_image_url,
            'course_about':
            get_link_for_about_page(course_overview),
            'course_sharing_utm_parameters':
            get_encoded_course_sharing_utm_params(),
            'course_updates':
            reverse(
                'course-updates-list',
                kwargs={
                    'api_version': api_version,
                    'course_id': course_id
                },
                request=request,
            ),
            'course_handouts':
            reverse(
                'course-handouts-list',
                kwargs={
                    'api_version': api_version,
                    'course_id': course_id
                },
                request=request,
            ),
            'discussion_url':
            reverse(
                'discussion_course',
                kwargs={'course_id': course_id},
                request=request,
            ) if course_overview.is_discussion_tab_enabled() else None,

            # This is an old API that was removed as part of DEPR-4. We keep the
            # field present in case API parsers expect it, but this API is now
            # removed.
            'video_outline':
            None,
        }
Example #28
0
 def _attach_course_run_can_enroll(self, run_mode):
     run_mode['can_enroll'] = bool(has_access(self.user, 'enroll', self.course_overview))
Example #29
0
    def get(self, request, *args, **kwargs):  # pylint: disable=too-many-statements
        course_key_string = kwargs.get('course_key_string')
        course_key = CourseKey.from_string(course_key_string)
        course_usage_key = modulestore().make_course_usage_key(course_key)  # pylint: disable=unused-variable

        if course_home_legacy_is_active(course_key):
            raise Http404

        # Enable NR tracing for this view based on course
        monitoring_utils.set_custom_attribute('course_id', course_key_string)
        monitoring_utils.set_custom_attribute('user_id', request.user.id)
        monitoring_utils.set_custom_attribute('is_staff',
                                              request.user.is_staff)

        course = get_course_with_access(request.user,
                                        'load',
                                        course_key,
                                        check_if_enrolled=False)

        masquerade_object, request.user = setup_masquerade(
            request,
            course_key,
            staff_access=has_access(request.user, 'staff', course_key),
            reset_masquerade_data=True,
        )

        user_is_masquerading = is_masquerading(
            request.user, course_key, course_masquerade=masquerade_object)

        course_overview = get_course_overview_or_404(course_key)
        enrollment = CourseEnrollment.get_enrollment(request.user, course_key)
        enrollment_mode = getattr(enrollment, 'mode', None)
        allow_anonymous = COURSE_ENABLE_UNENROLLED_ACCESS_FLAG.is_enabled(
            course_key)
        allow_public = allow_anonymous and course.course_visibility == COURSE_VISIBILITY_PUBLIC
        allow_public_outline = allow_anonymous and course.course_visibility == COURSE_VISIBILITY_PUBLIC_OUTLINE

        # User locale settings
        user_timezone_locale = user_timezone_locale_prefs(request)
        user_timezone = user_timezone_locale['user_timezone']

        if course_home_legacy_is_active(course.id):
            dates_tab_link = request.build_absolute_uri(
                reverse('dates', args=[course.id]))
        else:
            dates_tab_link = get_learning_mfe_home_url(course_key=course.id,
                                                       url_fragment='dates')

        # Set all of the defaults
        access_expiration = None
        cert_data = None
        course_blocks = None
        course_goals = {
            'selected_goal': None,
            'weekly_learning_goal_enabled': False,
        }
        course_tools = CourseToolsPluginManager.get_enabled_course_tools(
            request, course_key)
        dates_widget = {
            'course_date_blocks': [],
            'dates_tab_link': dates_tab_link,
            'user_timezone': user_timezone,
        }
        enroll_alert = {
            'can_enroll': True,
            'extra_text': None,
        }
        handouts_html = None
        offer_data = None
        resume_course = {
            'has_visited_course': False,
            'url': None,
        }
        welcome_message_html = None

        is_enrolled = enrollment and enrollment.is_active
        is_staff = bool(has_access(request.user, 'staff', course_key))
        show_enrolled = is_enrolled or is_staff
        enable_proctored_exams = False
        if show_enrolled:
            course_blocks = get_course_outline_block_tree(
                request, course_key_string, request.user)
            date_blocks = get_course_date_blocks(course,
                                                 request.user,
                                                 request,
                                                 num_assignments=1)
            dates_widget['course_date_blocks'] = [
                block for block in date_blocks
                if not isinstance(block, TodaysDate)
            ]

            handouts_html = get_course_info_section(request, request.user,
                                                    course, 'handouts')
            welcome_message_html = get_current_update_for_user(request, course)

            offer_data = generate_offer_data(request.user, course_overview)
            access_expiration = get_access_expiration_data(
                request.user, course_overview)
            cert_data = get_cert_data(request.user, course,
                                      enrollment.mode) if is_enrolled else None

            enable_proctored_exams = course_overview.enable_proctored_exams

            if (is_enrolled and ENABLE_COURSE_GOALS.is_enabled(course_key)):
                course_goals['weekly_learning_goal_enabled'] = True
                selected_goal = get_course_goal(request.user, course_key)
                if selected_goal:
                    course_goals['selected_goal'] = {
                        'days_per_week':
                        selected_goal.days_per_week,
                        'subscribed_to_reminders':
                        selected_goal.subscribed_to_reminders,
                    }

            try:
                resume_block = get_key_to_last_completed_block(
                    request.user, course.id)
                resume_course['has_visited_course'] = True
                resume_path = reverse('jump_to',
                                      kwargs={
                                          'course_id': course_key_string,
                                          'location': str(resume_block)
                                      })
                resume_course['url'] = request.build_absolute_uri(resume_path)
            except UnavailableCompletionData:
                start_block = get_start_block(course_blocks)
                resume_course['url'] = start_block['lms_web_url']

        elif allow_public_outline or allow_public or user_is_masquerading:
            course_blocks = get_course_outline_block_tree(
                request, course_key_string, None)
            if allow_public or user_is_masquerading:
                handouts_html = get_course_info_section(
                    request, request.user, course, 'handouts')

        if not is_enrolled:
            if CourseMode.is_masters_only(course_key):
                enroll_alert['can_enroll'] = False
                enroll_alert['extra_text'] = _(
                    'Please contact your degree administrator or '
                    '{platform_name} Support if you have questions.').format(
                        platform_name=settings.PLATFORM_NAME)
            elif course_is_invitation_only(course):
                enroll_alert['can_enroll'] = False

        # Sometimes there are sequences returned by Course Blocks that we
        # don't actually want to show to the user, such as when a sequence is
        # composed entirely of units that the user can't access. The Learning
        # Sequences API knows how to roll this up, so we use it determine which
        # sequences we should remove from course_blocks.
        #
        # The long term goal is to remove the Course Blocks API call entirely,
        # so this is a tiny first step in that migration.
        if course_blocks:
            user_course_outline = get_user_course_outline(
                course_key, request.user, datetime.now(tz=timezone.utc))
            available_seq_ids = {
                str(usage_key)
                for usage_key in user_course_outline.sequences
            }

            # course_blocks is a reference to the root of the course, so we go
            # through the chapters (sections) to look for sequences to remove.
            for chapter_data in course_blocks['children']:
                chapter_data['children'] = [
                    seq_data for seq_data in chapter_data['children'] if
                    (seq_data['id'] in available_seq_ids or
                     # Edge case: Sometimes we have weird course structures.
                     # We expect only sequentials here, but if there is
                     # another type, just skip it (don't filter it out).
                     seq_data['type'] != 'sequential')
                ] if 'children' in chapter_data else []

        user_has_passing_grade = False
        if not request.user.is_anonymous:
            user_grade = CourseGradeFactory().read(request.user, course)
            if user_grade:
                user_has_passing_grade = user_grade.passed

        data = {
            'access_expiration': access_expiration,
            'cert_data': cert_data,
            'course_blocks': course_blocks,
            'course_goals': course_goals,
            'course_tools': course_tools,
            'dates_widget': dates_widget,
            'enable_proctored_exams': enable_proctored_exams,
            'enroll_alert': enroll_alert,
            'enrollment_mode': enrollment_mode,
            'handouts_html': handouts_html,
            'has_ended': course.has_ended(),
            'offer': offer_data,
            'resume_course': resume_course,
            'user_has_passing_grade': user_has_passing_grade,
            'welcome_message_html': welcome_message_html,
        }
        context = self.get_serializer_context()
        context['course_overview'] = course_overview
        context['enable_links'] = show_enrolled or allow_public
        context['enrollment'] = enrollment
        serializer = self.get_serializer_class()(data, context=context)

        return Response(serializer.data)
Example #30
0
    def get(self, request, *args, **kwargs):
        course_key_string = kwargs.get('course_key_string')
        course_key = CourseKey.from_string(course_key_string)

        if not course_home_mfe_dates_tab_is_active(course_key):
            return Response(status=status.HTTP_404_NOT_FOUND)

        # Enable NR tracing for this view based on course
        monitoring_utils.set_custom_metric('course_id', course_key_string)
        monitoring_utils.set_custom_metric('user_id', request.user.id)
        monitoring_utils.set_custom_metric('is_staff', request.user.is_staff)

        course = get_course_with_access(request.user,
                                        'load',
                                        course_key,
                                        check_if_enrolled=False)

        _, request.user = setup_masquerade(
            request,
            course_key,
            staff_access=has_access(request.user, 'staff', course_key),
            reset_masquerade_data=True,
        )

        blocks = get_course_date_blocks(course,
                                        request.user,
                                        request,
                                        include_access=True,
                                        include_past_dates=True)
        missed_deadlines, missed_gated_content = dates_banner_should_display(
            course_key, request.user)

        learner_is_full_access = not ContentTypeGatingConfig.enabled_for_enrollment(
            user=request.user,
            course_key=course_key,
        )

        # User locale settings
        user_timezone_locale = user_timezone_locale_prefs(request)
        user_timezone = user_timezone_locale['user_timezone']

        data = {
            'has_ended':
            course.has_ended(),
            'course_date_blocks':
            [block for block in blocks if not isinstance(block, TodaysDate)],
            'missed_deadlines':
            missed_deadlines,
            'missed_gated_content':
            missed_gated_content,
            'learner_is_full_access':
            learner_is_full_access,
            'user_timezone':
            user_timezone,
            'verified_upgrade_link':
            verified_upgrade_deadline_link(request.user, course=course),
        }
        context = self.get_serializer_context()
        context['learner_is_full_access'] = learner_is_full_access
        serializer = self.get_serializer_class()(data, context=context)

        return Response(serializer.data)
Example #31
0
def list_course_keys(request, username, role):
    """
    Yield all available CourseKeys for the user having the given role.

    The courses returned include those for which the user identified by
    `username` has the given role.  Additionally, the logged in user
    should have permission to view courses available to that user.

    Note: This function does not use branding to determine courses.

    Arguments:
        request (HTTPRequest):
            Used to identify the logged-in user and to instantiate the course
            module to retrieve the course about description
        username (string):
            The name of the user the logged-in user would like to be
            identified as

    Keyword Arguments:
        role (string):
            Course keys are filtered such that only those for which the
            user has the specified role are returned.

    Return value:
        Yield `CourseKey` objects representing the collection of courses.

    """
    user = get_effective_user(request.user, username)

    all_course_keys = CourseOverview.get_all_course_keys()

    # Global staff have access to all courses. Filter courses for non-global staff.
    if GlobalStaff().has_user(user):
        return all_course_keys

    if role == 'staff':
        # This short-circuit implementation bypasses has_access() which we think is too slow for some users when
        # evaluating staff-level course access for Insights.  Various tickets have context on this issue: CR-2487,
        # TNL-7448, DESUPPORT-416, and probably more.
        #
        # This is a simplified implementation that does not consider org-level access grants (e.g. when course_id is
        # empty).
        filtered_course_keys = (
            CourseAccessRole.objects.filter(
                user=user,
                # Having the instructor role implies staff access.
                role__in=['staff', 'instructor'],
            )
            # We need to check against CourseOverview so that we don't return any Libraries.
            .extra(tables=['course_overviews_courseoverview'],
                   where=['course_id = course_overviews_courseoverview.id'])
            # For good measure, make sure we don't return empty course IDs.
            .exclude(course_id=CourseKeyField.Empty).order_by(
                'course_id').values_list('course_id', flat=True).distinct())
    else:
        # This is the original implementation which still covers the case where role = "instructor":
        filtered_course_keys = LazySequence(
            (course_key for course_key in all_course_keys
             if has_access(user, role, course_key)),
            est_len=len(all_course_keys))
    return filtered_course_keys
Example #32
0
def can_access_all_blocks(requesting_user, course_key):
    """
    Returns whether the requesting_user can access all the blocks
    in the course.
    """
    return has_access(requesting_user, CourseStaffRole.ROLE, course_key)
Example #33
0
def preprocess_collection(user, course, collection):
    """
    Prepare `collection(notes_list)` provided by edx-notes-api
    for rendering in a template:
       add information about ancestor blocks,
       convert "updated" to date

    Raises:
        ItemNotFoundError - when appropriate module is not found.
    """
    # pylint: disable=too-many-statements

    store = modulestore()
    filtered_collection = list()
    cache = {}
    include_path_info = ('course_structure'
                         not in settings.NOTES_DISABLED_TABS)
    with store.bulk_operations(course.id):
        for model in collection:
            update = {
                u"updated": dateutil_parse(model["updated"]),
            }

            model.update(update)
            usage_id = model["usage_id"]
            if usage_id in list(cache.keys()):
                model.update(cache[usage_id])
                filtered_collection.append(model)
                continue

            usage_key = UsageKey.from_string(usage_id)
            # Add a course run if necessary.
            usage_key = usage_key.replace(
                course_key=store.fill_in_run(usage_key.course_key))

            try:
                item = store.get_item(usage_key)
            except ItemNotFoundError:
                log.debug(u"Module not found: %s", usage_key)
                continue

            if not has_access(user, "load", item, course_key=course.id):
                log.debug(u"User %s does not have an access to %s", user, item)
                continue

            unit = get_parent_unit(item)
            if unit is None:
                log.debug(u"Unit not found: %s", usage_key)
                continue

            if include_path_info:
                section = unit.get_parent()
                if not section:
                    log.debug(u"Section not found: %s", usage_key)
                    continue
                if section.location in list(cache.keys()):
                    usage_context = cache[section.location]
                    usage_context.update({
                        "unit":
                        get_module_context(course, unit),
                    })
                    model.update(usage_context)
                    cache[usage_id] = cache[unit.location] = usage_context
                    filtered_collection.append(model)
                    continue

                chapter = section.get_parent()
                if not chapter:
                    log.debug(u"Chapter not found: %s", usage_key)
                    continue
                if chapter.location in list(cache.keys()):
                    usage_context = cache[chapter.location]
                    usage_context.update({
                        "unit":
                        get_module_context(course, unit),
                        "section":
                        get_module_context(course, section),
                    })
                    model.update(usage_context)
                    cache[usage_id] = cache[unit.location] = cache[
                        section.location] = usage_context
                    filtered_collection.append(model)
                    continue

            usage_context = {
                "unit": get_module_context(course, unit),
                "section": get_module_context(course, section)
                if include_path_info else {},
                "chapter": get_module_context(course, chapter)
                if include_path_info else {},
            }
            model.update(usage_context)
            if include_path_info:
                cache[section.location] = cache[
                    chapter.location] = usage_context

            cache[usage_id] = cache[unit.location] = usage_context
            filtered_collection.append(model)

    return filtered_collection
 def should_remove(self, user):
     """ Test to see if this result should be removed due to access restriction """
     if has_access(user, 'staff', self.get_course_key()):
         return False
     return self.get_usage_key() not in self.get_course_blocks(
         user).get_block_keys()
Example #35
0
def reset_course_deadlines(request):
    """
    Set the start_date of a schedule to today, which in turn will adjust due dates for
    sequentials belonging to a self paced course

    Request Parameters:
        course_key: course key
        research_event_data: any data that should be included in the research tracking event
            Example: sending the location of where the reset deadlines banner (i.e. outline-tab)

    IMPORTANT NOTE: If updates are happening to the logic here, ALSO UPDATE the `reset_course_deadlines`
    function in common/djangoapps/util/views.py as well.
    """
    course_key = request.data.get('course_key', None)
    research_event_data = request.data.get('research_event_data', {})

    # If body doesnt contain 'course_key', return 400 to client.
    if not course_key:
        raise ParseError(_("'course_key' is required."))

    try:
        course_key = CourseKey.from_string(course_key)
        course_masquerade, user = setup_masquerade(
            request, course_key, has_access(request.user, 'staff', course_key))

        # We ignore the missed_deadlines because this endpoint is used in the Learning MFE for
        # learners who have remaining attempts on a problem and reset their due dates in order to
        # submit additional attempts. This can apply for 'completed' (submitted) content that would
        # not be marked as past_due
        _missed_deadlines, missed_gated_content = dates_banner_should_display(
            course_key, user)
        if not missed_gated_content:
            reset_self_paced_schedule(user, course_key)

            course_overview = course_detail(request, user.username, course_key)
            # For context here, research_event_data should already contain `location` indicating
            # the page/location dates were reset from and could also contain `block_id` if reset
            # within courseware.
            research_event_data.update({
                'courserun_key':
                str(course_key),
                'is_masquerading':
                is_masquerading(user, course_key, course_masquerade),
                'is_staff':
                has_access(user, 'staff', course_key).has_access,
                'org_key':
                course_overview.display_org_with_default,
                'user_id':
                user.id,
            })
            tracker.emit('edx.ui.lms.reset_deadlines.clicked',
                         research_event_data)

        body_link = get_learning_mfe_home_url(course_key=course_key,
                                              url_fragment='dates')

        return Response({
            'body':
            format_html('<a href="{}">{}</a>', body_link, _('View all dates')),
            'header':
            _('Your due dates have been successfully shifted to help you stay on track.'
              ),
            'link':
            body_link,
            'link_text':
            _('View all dates'),
            'message':
            _('Deadlines successfully reset.'),
        })
    except Exception as reset_deadlines_exception:
        log.exception('Error occurred while trying to reset deadlines!')
        raise UnableToResetDeadlines from reset_deadlines_exception