Example #1
0
def render_body(context, **pageargs):
    __M_caller = context.caller_stack._push_frame()
    try:
        __M_locals = __M_dict_builtin(pageargs=pageargs)
        submit_button_submitting = context.get('submit_button_submitting',
                                               UNDEFINED)
        save_message = context.get('save_message', UNDEFINED)
        short_id = context.get('short_id', UNDEFINED)
        answer_notification_message = context.get(
            'answer_notification_message', UNDEFINED)
        should_enable_next_hint = context.get('should_enable_next_hint',
                                              UNDEFINED)
        id = context.get('id', UNDEFINED)
        reset_button = context.get('reset_button', UNDEFINED)
        has_saved_answers = context.get('has_saved_answers', UNDEFINED)
        demand_hint_possible = context.get('demand_hint_possible', UNDEFINED)
        should_enable_submit_button = context.get(
            'should_enable_submit_button', UNDEFINED)
        submit_button = context.get('submit_button', UNDEFINED)
        attempts_allowed = context.get('attempts_allowed', UNDEFINED)
        save_button = context.get('save_button', UNDEFINED)
        problem = context.get('problem', UNDEFINED)
        attempts_used = context.get('attempts_used', UNDEFINED)
        answer_available = context.get('answer_available', UNDEFINED)
        answer_notification_type = context.get('answer_notification_type',
                                               UNDEFINED)
        __M_writer = context.writer()
        __M_writer(u'\n')
        __M_writer(u'\n\n')
        __M_writer(u'\n<h3 class="hd hd-3 problem-header" id="')
        __M_writer(filters.html_escape(filters.decode.utf8(short_id)))
        __M_writer(u'-problem-title" aria-describedby="')
        __M_writer(filters.html_escape(filters.decode.utf8(id)))
        __M_writer(u'-problem-progress" tabindex="-1">\n  ')
        __M_writer(filters.html_escape(filters.decode.utf8(problem['name'])))
        __M_writer(u'\n</h3>\n\n<div class="problem-progress" id="')
        __M_writer(filters.html_escape(filters.decode.utf8(id)))
        __M_writer(u'-problem-progress"></div>\n\n<div class="problem">\n  ')
        __M_writer(
            filters.html_escape(filters.decode.utf8(HTML(problem['html']))))
        __M_writer(
            u'\n  <div class="action">\n    <input type="hidden" name="problem_id" value="'
        )
        __M_writer(filters.html_escape(filters.decode.utf8(problem['name'])))
        __M_writer(u'" />\n')
        if demand_hint_possible:
            __M_writer(u'      <div class="problem-hint">\n        ')
            runtime._include_file(context,
                                  u'problem_notifications.html',
                                  _template_uri,
                                  notification_name='hint',
                                  notification_type='problem-hint',
                                  notification_icon='fa-question',
                                  notification_message='')
            __M_writer(u'\n      </div>\n')
        __M_writer(
            u'\n    <div class="submit-attempt-container">\n      <button type="button" class="submit btn-brand" data-submitting="'
        )
        __M_writer(
            filters.html_escape(filters.decode.utf8(submit_button_submitting)))
        __M_writer(u'" data-value="')
        __M_writer(filters.html_escape(filters.decode.utf8(submit_button)))
        __M_writer(u'" data-should-enable-submit-button="')
        __M_writer(
            filters.html_escape(
                filters.decode.utf8(should_enable_submit_button)))
        __M_writer(u'" aria-describedby="submission_feedback_')
        __M_writer(filters.html_escape(filters.decode.utf8(short_id)))
        __M_writer(u'" ')
        __M_writer(
            filters.html_escape(
                filters.decode.utf8(
                    '' if should_enable_submit_button else 'disabled')))
        __M_writer(u'>\n          <span class="submit-label">')
        __M_writer(filters.html_escape(filters.decode.utf8(submit_button)))
        __M_writer(
            u'</span>\n      </button>\n      <div class="submission-feedback" id="submission_feedback_'
        )
        __M_writer(filters.html_escape(filters.decode.utf8(short_id)))
        __M_writer(u'">\n')
        if attempts_allowed:
            __M_writer(u'          ')
            __M_writer(
                filters.html_escape(
                    filters.decode.utf8(
                        ungettext(
                            "You have used {num_used} of {num_total} attempt",
                            "You have used {num_used} of {num_total} attempts",
                            attempts_allowed).format(
                                num_used=attempts_used,
                                num_total=attempts_allowed))))
            __M_writer(u'\n')
        __M_writer(u'        <span class="sr">')
        __M_writer(
            filters.html_escape(
                filters.decode.utf8(
                    _("Some problems have options such as save, reset, hints, or show answer. These options follow the Submit button."
                      ))))
        __M_writer(
            u'</span>\n      </div>\n    </div>\n    <div class="problem-action-buttons-wrapper">\n'
        )
        if demand_hint_possible:
            __M_writer(
                u'      <span class="problem-action-button-wrapper">\n          <button type="button" class="hint-button problem-action-btn btn-default btn-small" data-value="'
            )
            __M_writer(filters.html_escape(filters.decode.utf8(_('Hint'))))
            __M_writer(u'" ')
            __M_writer(
                filters.html_escape(
                    filters.decode.utf8(
                        '' if should_enable_next_hint else 'disabled')))
            __M_writer(
                u'><span class="icon fa fa-question" aria-hidden="true"></span>'
            )
            __M_writer(filters.html_escape(filters.decode.utf8(_('Hint'))))
            __M_writer(u'</button>\n      </span>\n')
        if save_button:
            __M_writer(
                u'      <span class="problem-action-button-wrapper">\n          <button type="button" class="save problem-action-btn btn-default btn-small" data-value="'
            )
            __M_writer(filters.html_escape(filters.decode.utf8(_('Save'))))
            __M_writer(
                u'">\n              <span class="icon fa fa-floppy-o" aria-hidden="true"></span>\n              <span aria-hidden="true">'
            )
            __M_writer(filters.html_escape(filters.decode.utf8(_('Save'))))
            __M_writer(u'</span>\n              <span class="sr">')
            __M_writer(
                filters.html_escape(filters.decode.utf8(
                    _("Save your answer"))))
            __M_writer(u'</span>\n          </button>\n      </span>\n')
        if reset_button:
            __M_writer(
                u'      <span class="problem-action-button-wrapper">\n          <button type="button" class="reset problem-action-btn btn-default btn-small" data-value="'
            )
            __M_writer(filters.html_escape(filters.decode.utf8(_('Reset'))))
            __M_writer(
                u'"><span class="icon fa fa-refresh" aria-hidden="true"></span><span aria-hidden="true">'
            )
            __M_writer(filters.html_escape(filters.decode.utf8(_('Reset'))))
            __M_writer(u'</span><span class="sr">')
            __M_writer(
                filters.html_escape(filters.decode.utf8(
                    _("Reset your answer"))))
            __M_writer(u'</span></button>\n      </span>\n')
        if answer_available:
            __M_writer(
                u'      <span class="problem-action-button-wrapper">\n          <button type="button" class="show problem-action-btn btn-default btn-small" aria-describedby="'
            )
            __M_writer(filters.html_escape(filters.decode.utf8(short_id)))
            __M_writer(
                u'-problem-title"><span class="icon fa fa-info-circle" aria-hidden="true"></span><span class="show-label">'
            )
            __M_writer(
                filters.html_escape(filters.decode.utf8(_('Show Answer'))))
            __M_writer(u'</span></button>\n      </span>\n')
        __M_writer(u'    </div>\n  </div>\n    ')
        runtime._include_file(context,
                              u'problem_notifications.html',
                              _template_uri,
                              notification_type='warning',
                              notification_icon='fa-exclamation-circle',
                              notification_name='gentle-alert',
                              notification_message='')
        __M_writer(u'\n')
        if answer_notification_type:
            if 'correct' == answer_notification_type:
                __M_writer(u'            ')
                runtime._include_file(
                    context,
                    u'problem_notifications.html',
                    _template_uri,
                    notification_type='success',
                    notification_icon='fa-check',
                    notification_name='submit',
                    is_hidden=False,
                    notification_message=answer_notification_message)
                __M_writer(u'\n')
            if 'incorrect' == answer_notification_type:
                __M_writer(u'            ')
                runtime._include_file(
                    context,
                    u'problem_notifications.html',
                    _template_uri,
                    notification_type='error',
                    notification_icon='fa-close',
                    notification_name='submit',
                    is_hidden=False,
                    notification_message=answer_notification_message)
                __M_writer(u'\n')
            if 'partially-correct' == answer_notification_type:
                __M_writer(u'            ')
                runtime._include_file(
                    context,
                    u'problem_notifications.html',
                    _template_uri,
                    notification_type='success',
                    notification_icon='fa-asterisk',
                    notification_name='submit',
                    is_hidden=False,
                    notification_message=answer_notification_message)
                __M_writer(u'\n')
            if 'submitted' == answer_notification_type:
                __M_writer(u'            ')
                runtime._include_file(
                    context,
                    u'problem_notifications.html',
                    _template_uri,
                    notification_type='general',
                    notification_icon='fa-info-circle',
                    notification_name='submit',
                    is_hidden=False,
                    notification_message=answer_notification_message)
                __M_writer(u'\n')
        __M_writer(u'    ')
        runtime._include_file(context,
                              u'problem_notifications.html',
                              _template_uri,
                              notification_type='warning',
                              notification_icon='fa-save',
                              notification_name='save',
                              notification_message=save_message,
                              is_hidden=not has_saved_answers)
        __M_writer(u'\n    ')

        notification_message = _('Answers are displayed within the problem')

        __M_locals_builtin_stored = __M_locals_builtin()
        __M_locals.update(
            __M_dict_builtin([(__M_key, __M_locals_builtin_stored[__M_key])
                              for __M_key in ['notification_message']
                              if __M_key in __M_locals_builtin_stored]))
        __M_writer(u'\n    ')
        runtime._include_file(context,
                              u'problem_notifications.html',
                              _template_uri,
                              notification_type='general',
                              notification_icon='fa-info-circle',
                              notification_name='show-answer',
                              notification_message=notification_message,
                              is_hidden=True)
        __M_writer(u'\n</div>\n')
        return ''
    finally:
        context.caller_stack._pop_frame()
Example #2
0
def activate_account(request, key):
    """
    When link in activation e-mail is clicked
    """
    # If request is in Studio call the appropriate view
    if theming_helpers.get_project_root_name().lower() == u'cms':
        monitoring_utils.set_custom_attribute('student_activate_account', 'cms')
        return activate_account_studio(request, key)

    # TODO: Use custom attribute to determine if there are any `activate_account` calls for cms in Production.
    # If not, the templates wouldn't be needed for cms, but we still need a way to activate for cms tests.
    monitoring_utils.set_custom_attribute('student_activate_account', 'lms')
    activation_message_type = None
    try:
        registration = Registration.objects.get(activation_key=key)
    except (Registration.DoesNotExist, Registration.MultipleObjectsReturned):
        activation_message_type = 'error'
        messages.error(
            request,
            HTML(_(
                '{html_start}Your account could not be activated{html_end}'
                'Something went wrong, please <a href="{support_url}">contact support</a> to resolve this issue.'
            )).format(
                support_url=configuration_helpers.get_value(
                    'ACTIVATION_EMAIL_SUPPORT_LINK', settings.ACTIVATION_EMAIL_SUPPORT_LINK
                ) or settings.SUPPORT_SITE_LINK,
                html_start=HTML('<p class="message-title">'),
                html_end=HTML('</p>'),
            ),
            extra_tags='account-activation aa-icon'
        )
    else:
        if registration.user.is_active:
            activation_message_type = 'info'
            messages.info(
                request,
                HTML(_('{html_start}This account has already been activated.{html_end}')).format(
                    html_start=HTML('<p class="message-title">'),
                    html_end=HTML('</p>'),
                ),
                extra_tags='account-activation aa-icon',
            )
        else:
            registration.activate()
            # Success message for logged in users.
            message = _('{html_start}Success{html_end} You have activated your account.')

            tracker.emit(
                USER_ACCOUNT_ACTIVATED,
                {
                    "user_id": registration.user.id,
                }
            )

            if not request.user.is_authenticated:
                # Success message for logged out users
                message = _(
                    '{html_start}Success! You have activated your account.{html_end}'
                    'You will now receive email updates and alerts from us related to'
                    ' the courses you are enrolled in. Sign In to continue.'
                )

            # Add message for later use.
            activation_message_type = 'success'
            messages.success(
                request,
                HTML(message).format(
                    html_start=HTML('<p class="message-title">'),
                    html_end=HTML('</p>'),
                ),
                extra_tags='account-activation aa-icon',
            )

    if should_redirect_to_authn_microfrontend() and not request.user.is_authenticated:
        url_path = '/login?account_activation_status={}'.format(activation_message_type)
        return redirect(settings.AUTHN_MICROFRONTEND_URL + url_path)

    return redirect('dashboard')
def _register_course_goal_message(request, course):
    """
    Register a message to let a learner specify a course goal.
    """
    course_goal_options = get_course_goal_options()
    goal_choices_html = Text(_(
        'To start, set a course goal by selecting the option below that best describes '
        u'your learning plan. {goal_options_container}'
    )).format(
        goal_options_container=HTML('<div class="row goal-options-container">')
    )

    # Add the dismissible option for users that are unsure of their goal
    goal_choices_html += Text(
        '{initial_tag}{choice}{closing_tag}'
    ).format(
        initial_tag=HTML(
            u'<div tabindex="0" aria-label="{aria_label_choice}" class="goal-option dismissible" '
            'data-choice="{goal_key}">'
        ).format(
            goal_key=GOAL_KEY_CHOICES.unsure,
            aria_label_choice=Text(_(u"Set goal to: {choice}")).format(
                choice=course_goal_options[GOAL_KEY_CHOICES.unsure],
            ),
        ),
        choice=Text(_('{choice}')).format(
            choice=course_goal_options[GOAL_KEY_CHOICES.unsure],
        ),
        closing_tag=HTML('</div>'),
    )

    # Add the option to set a goal to earn a certificate,
    # complete the course or explore the course
    course_goals_by_commitment_level = valid_course_goals_ordered()
    for goal in course_goals_by_commitment_level:
        goal_key, goal_text = goal
        goal_choices_html += HTML(
            '{initial_tag}{goal_text}{closing_tag}'
        ).format(
            initial_tag=HTML(
                u'<button tabindex="0" aria-label="{aria_label_choice}" class="goal-option btn-outline-primary" '
                'data-choice="{goal_key}">'
            ).format(
                goal_key=goal_key,
                aria_label_choice=Text(_(u"Set goal to: {goal_text}")).format(
                    goal_text=Text(_(goal_text))
                )
            ),
            goal_text=goal_text,
            closing_tag=HTML('</button>')
        )

    CourseHomeMessages.register_info_message(
        request,
        HTML('{goal_choices_html}{closing_tag}').format(
            goal_choices_html=goal_choices_html,
            closing_tag=HTML('</div>')
        ),
        title=Text(_(u'Welcome to {course_display_name}')).format(
            course_display_name=course.display_name
        )
    )
Example #4
0
def activate_account(request, key):
    """
    When link in activation e-mail is clicked
    """
    # If request is in Studio call the appropriate view
    if theming_helpers.get_project_root_name().lower() == u'cms':
        return activate_account_studio(request, key)

    try:
        registration = Registration.objects.get(activation_key=key)
    except (Registration.DoesNotExist, Registration.MultipleObjectsReturned):
        messages.error(
            request,
            HTML(
                _('{html_start}Your account could not be activated{html_end}'
                  'Something went wrong, please <a href="{support_url}">contact support</a> to resolve this issue.'
                  )).format(
                      support_url=configuration_helpers.get_value(
                          'SUPPORT_SITE_LINK', settings.SUPPORT_SITE_LINK),
                      html_start=HTML('<p class="message-title">'),
                      html_end=HTML('</p>'),
                  ),
            extra_tags='account-activation aa-icon')
    else:
        if registration.user.is_active:
            messages.info(
                request,
                HTML(
                    _('{html_start}This account has already been activated.{html_end}'
                      )).format(
                          html_start=HTML('<p class="message-title">'),
                          html_end=HTML('</p>'),
                      ),
                extra_tags='account-activation aa-icon',
            )
        elif waffle().is_enabled(PREVENT_AUTH_USER_WRITES):
            messages.error(
                request,
                HTML(u'{html_start}{message}{html_end}').format(
                    message=Text(SYSTEM_MAINTENANCE_MSG),
                    html_start=HTML('<p class="message-title">'),
                    html_end=HTML('</p>'),
                ),
                extra_tags='account-activation aa-icon',
            )
        else:
            registration.activate()
            # Success message for logged in users.
            message = _(
                '{html_start}Success{html_end} You have activated your account.'
            )

            if not request.user.is_authenticated:
                # Success message for logged out users
                message = _(
                    '{html_start}Success! You have activated your account.{html_end}'
                    'You will now receive email updates and alerts from us related to'
                    ' the courses you are enrolled in. Sign In to continue.')

            # Add message for later use.
            messages.success(
                request,
                HTML(message).format(
                    html_start=HTML('<p class="message-title">'),
                    html_end=HTML('</p>'),
                ),
                extra_tags='account-activation aa-icon',
            )

    return redirect('dashboard')
Example #5
0
def render_body(context,**pageargs):
    __M_caller = context.caller_stack._push_frame()
    try:
        __M_locals = __M_dict_builtin(pageargs=pageargs)
        loop = __M_loop = runtime.LoopStack()
        def certificate_block():
            return render_certificate_block(context._locals(__M_locals))
        int = context.get('int', UNDEFINED)
        float = context.get('float', UNDEFINED)
        def pagetitle():
            return render_pagetitle(context._locals(__M_locals))
        course = context.get('course', UNDEFINED)
        static = _mako_get_namespace(context, 'static')
        unicode = context.get('unicode', UNDEFINED)
        def bodyclass():
            return render_bodyclass(context._locals(__M_locals))
        credit_course_requirements = context.get('credit_course_requirements', UNDEFINED)
        def js_extra():
            return render_js_extra(context._locals(__M_locals))
        studio_url = context.get('studio_url', UNDEFINED)
        getattr = context.get('getattr', UNDEFINED)
        dict = context.get('dict', UNDEFINED)
        course_expiration_fragment = context.get('course_expiration_fragment', UNDEFINED)
        user_timezone = context.get('user_timezone', UNDEFINED)
        len = context.get('len', UNDEFINED)
        student = context.get('student', UNDEFINED)
        user_language = context.get('user_language', UNDEFINED)
        staff_access = context.get('staff_access', UNDEFINED)
        certificate_data = context.get('certificate_data', UNDEFINED)
        progress_graph = _mako_get_namespace(context, 'progress_graph')
        request = context.get('request', UNDEFINED)
        grade_summary = context.get('grade_summary', UNDEFINED)
        def headextra():
            return render_headextra(context._locals(__M_locals))
        courseware_summary = context.get('courseware_summary', UNDEFINED)
        __M_writer = context.writer()
        __M_writer(u'\n')
        __M_writer(u'\n')
        __M_writer(u'\n')
        __M_writer(u'\n')
        __M_writer(u'\n\n')

        username = get_enterprise_learner_generic_name(request) or student.username
        
        
        __M_locals_builtin_stored = __M_locals_builtin()
        __M_locals.update(__M_dict_builtin([(__M_key, __M_locals_builtin_stored[__M_key]) for __M_key in ['username'] if __M_key in __M_locals_builtin_stored]))
        __M_writer(u'\n\n')
        if 'parent' not in context._data or not hasattr(context._data['parent'], 'bodyclass'):
            context['self'].bodyclass(**pageargs)
        

        __M_writer(u'\n\n')
        if 'parent' not in context._data or not hasattr(context._data['parent'], 'headextra'):
            context['self'].headextra(**pageargs)
        

        __M_writer(u'\n\n\n')
        __M_writer(u'\n\n')
        if 'parent' not in context._data or not hasattr(context._data['parent'], 'pagetitle'):
            context['self'].pagetitle(**pageargs)
        

        __M_writer(u'\n\n')
        if 'parent' not in context._data or not hasattr(context._data['parent'], 'js_extra'):
            context['self'].js_extra(**pageargs)
        

        __M_writer(u'\n\n')
        runtime._include_file(context, u'/courseware/course_navigation.html', _template_uri, active_page='progress')
        __M_writer(u'\n\n<main id="main" aria-label="Content" tabindex="-1">\n    <div class="container">\n        <div class="profile-wrapper">\n            <section class="course-info" id="course-info-progress"\n')
        if getattr(course, 'language'):
            __M_writer(u'                lang="')
            __M_writer(filters.html_escape(filters.decode.utf8(course.language)))
            __M_writer(u'"\n')
        __M_writer(u'              >\n')
        if staff_access and studio_url is not None:
            __M_writer(u'                <div class="wrap-instructor-info">\n                    <a class="instructor-info-action studio-view" href="')
            __M_writer(filters.html_escape(filters.decode.utf8(studio_url)))
            __M_writer(u'">')
            __M_writer(filters.html_escape(filters.decode.utf8(_("View Grading in studio"))))
            __M_writer(u'</a>\n                </div>\n')
        __M_writer(u'                <h2 class="hd hd-2 progress-certificates-title">\n                    ')
        __M_writer(filters.html_escape(filters.decode.utf8(_("Course Progress for Student '{username}' ({email})").format(username=username, email=student.email))))
        __M_writer(u'\n                </h2>\n')
        if course_expiration_fragment:
            __M_writer(u'                    ')
            __M_writer(filters.html_escape(filters.decode.utf8(HTML(course_expiration_fragment.content))))
            __M_writer(u'\n')
        __M_writer(u'                <div class="wrapper-msg wrapper-auto-cert">\n                    <div id="errors-info" class="errors-info"></div>\n                    ')
        if 'parent' not in context._data or not hasattr(context._data['parent'], 'certificate_block'):
            context['self'].certificate_block(**pageargs)
        

        __M_writer(u'\n                </div>\n\n')
        if not course.disable_progress_graph:
            __M_writer(u'                <div class="grade-detail-graph" id="grade-detail-graph"></div>\n')
        __M_writer(u'\n')
        if credit_course_requirements:
            __M_writer(u'                <section class="credit-eligibility">\n                    <h3 class="hd hd-4 eligibility-heading">')
            __M_writer(filters.html_escape(filters.decode.utf8(_("Requirements for Course Credit"))))
            __M_writer(u'</h3>\n                    <div class="credit-eligibility-container">\n')
            if credit_course_requirements['eligibility_status'] == 'not_eligible':
                __M_writer(u'                        <span class="eligibility_msg">')
                __M_writer(filters.html_escape(filters.decode.utf8(_("{student_name}, you are no longer eligible for credit in this course.").format(student_name=student.profile.name))))
                __M_writer(u'</span>\n')
            elif credit_course_requirements['eligibility_status'] == 'eligible':
                __M_writer(u'                        <span class="eligibility_msg">\n                            ')
                __M_writer(filters.html_escape(filters.decode.utf8(Text(_("{student_name}, you have met the requirements for credit in this course. {a_start}Go to your dashboard{a_end} to purchase course credit.")).format(
                                student_name=student.profile.name,
                                a_start=HTML("<a href={url}>").format(url=reverse('dashboard')),
                                a_end=HTML("</a>")
                            ))))
                __M_writer(u'\n                        </span>\n')
            elif credit_course_requirements['eligibility_status'] == 'partial_eligible':
                __M_writer(u'                        <span>')
                __M_writer(filters.html_escape(filters.decode.utf8(_("{student_name}, you have not yet met the requirements for credit.").format(student_name=student.profile.name))))
                __M_writer(u'</span>\n')
            __M_writer(u'\n                        <a href="')
            __M_writer(filters.html_escape(filters.decode.utf8(settings.CREDIT_HELP_LINK_URL)))
            __M_writer(u'" class="credit-help">\n                            <span class="fa fa-question" aria-hidden="true"></span>\n                            <span class="sr">')
            __M_writer(filters.html_escape(filters.decode.utf8(_("Information about course credit requirements"))))
            __M_writer(u'</span>\n                        </a><br />\n\n                        <div class="requirement-container" data-eligible="')
            __M_writer(filters.html_escape(filters.decode.utf8(credit_course_requirements['eligibility_status'])))
            __M_writer(u'">\n')
            for requirement in credit_course_requirements['requirements']:
                __M_writer(u'                            <div class="requirement">\n                                <div class="requirement-name">\n                                    ')
                __M_writer(filters.html_escape(filters.decode.utf8(_(requirement['display_name']))))
                __M_writer(u'\n')
                if requirement['namespace'] == 'grade':
                    __M_writer(u'                                    <span>')
                    __M_writer(filters.html_escape(filters.decode.utf8(int(requirement['criteria']['min_grade'] * 100))))
                    __M_writer(u'%</span>\n')
                __M_writer(u'                                </div>\n                                <div class="requirement-status">\n')
                if requirement['status']:
                    if requirement['status'] == 'submitted':
                        __M_writer(u'                                        <span class="requirement-submitted">')
                        __M_writer(filters.html_escape(filters.decode.utf8(_("Verification Submitted"))))
                        __M_writer(u'</span>\n')
                    elif requirement['status'] == 'failed':
                        __M_writer(u'                                        <span class="fa fa-times" aria-hidden="true"></span>\n                                        <span>')
                        __M_writer(filters.html_escape(filters.decode.utf8(_("Verification Failed" ))))
                        __M_writer(u'</span>\n')
                    elif requirement['status'] == 'declined':
                        __M_writer(u'                                        <span class="fa fa-times" aria-hidden="true"></span>\n                                        <span>')
                        __M_writer(filters.html_escape(filters.decode.utf8(_("Verification Declined" ))))
                        __M_writer(u'</span>\n')
                    elif requirement['status'] == 'satisfied':
                        __M_writer(u'                                        <span class="fa fa-check" aria-hidden="true"></span>\n                                        <span class="localized-datetime" data-datetime="')
                        __M_writer(filters.html_escape(filters.decode.utf8(requirement['status_date'])))
                        __M_writer(u'" data-string="')
                        __M_writer(filters.html_escape(filters.decode.utf8(_('Completed by {date}'))))
                        __M_writer(u'" data-timezone="')
                        __M_writer(filters.html_escape(filters.decode.utf8(user_timezone)))
                        __M_writer(u'" data-language="')
                        __M_writer(filters.html_escape(filters.decode.utf8(user_language)))
                        __M_writer(u'"></span>\n')
                else:
                    __M_writer(u'                                    <span class="not-achieve">')
                    __M_writer(filters.html_escape(filters.decode.utf8(_("Upcoming"))))
                    __M_writer(u'</span>\n')
                __M_writer(u'                                </div>\n                            </div>\n')
            __M_writer(u'                        </div>\n                        <button class="detail-collapse">\n                            <span class="fa fa-caret-up" aria-hidden="true"></span>\n                            <span class="requirement-detail">')
            __M_writer(filters.html_escape(filters.decode.utf8(_("Less"))))
            __M_writer(u'</span>\n                        </button>\n                    </div>\n                </section>\n')
        __M_writer(u'\n')
        if courseware_summary:
            __M_writer(u'                <section class="chapters">\n                    <h2 class="sr">')
            __M_writer(filters.html_escape(filters.decode.utf8(_('Details for each chapter'))))
            __M_writer(u'</h2>\n')
            loop = __M_loop._enter(courseware_summary)
            try:
                for chapter in loop:
                    if not chapter['display_name'] == "hidden":
                        __M_writer(u'                        <section aria-labelledby="chapter_')
                        __M_writer(filters.html_escape(filters.decode.utf8(loop.index)))
                        __M_writer(u'">\n                            <h3 class="hd hd-3" id="chapter_')
                        __M_writer(filters.html_escape(filters.decode.utf8(loop.index)))
                        __M_writer(u'">')
                        __M_writer(filters.html_escape(filters.decode.utf8( chapter['display_name'])))
                        __M_writer(u'</h3>\n                            <div class="sections">\n')
                        for section in chapter['sections']:
                            __M_writer(u'                                    <div>\n                                        ')

                            earned = section.all_total.earned
                            total = section.all_total.possible
                            
                            percentageString = "{0:.0%}".format(section.percent_graded) if earned > 0 and total > 0 else ""
                            
                            
                            __M_locals_builtin_stored = __M_locals_builtin()
                            __M_locals.update(__M_dict_builtin([(__M_key, __M_locals_builtin_stored[__M_key]) for __M_key in ['percentageString','total','earned'] if __M_key in __M_locals_builtin_stored]))
                            __M_writer(u'\n                                        <h4 class="hd hd-4">\n                                            <a href="')
                            __M_writer(filters.html_escape(filters.decode.utf8(reverse('courseware_section', kwargs=dict(course_id=text_type(course.id), chapter=chapter['url_name'], section=section.url_name)))))
                            __M_writer(u'">\n                                                ')
                            __M_writer(filters.html_escape(filters.decode.utf8( section.display_name)))
                            __M_writer(u'\n')
                            if total > 0 or earned > 0:
                                __M_writer(u'                                                <span class="sr">\n                                                    ')
                                __M_writer(filters.html_escape(filters.decode.utf8(_("{earned} of {total} possible points").format(earned='{:.3n}'.format(float(earned)), total='{:.3n}'.format(float(total))))))
                                __M_writer(u'\n                                                </span>\n')
                            __M_writer(u'                                            </a>\n')
                            if total > 0 or earned > 0:
                                __M_writer(u'                                            <span> ')
                                __M_writer(filters.html_escape(filters.decode.utf8("({0:.3n}/{1:.3n}) {2}".format( float(earned), float(total), percentageString ))))
                                __M_writer(u'</span>\n')
                            __M_writer(u'                                        </h4>\n                                        <p>\n')
                            if section.format is not None:
                                __M_writer(u'                                                ')
                                __M_writer(filters.html_escape(filters.decode.utf8(section.format)))
                                __M_writer(u'\n')
                            if section.due is not None:
                                __M_writer(u'                                                <em class="localized-datetime" data-datetime="')
                                __M_writer(filters.html_escape(filters.decode.utf8(section.due)))
                                __M_writer(u'" data-string="')
                                __M_writer(filters.html_escape(filters.decode.utf8(_('due {date}'))))
                                __M_writer(u'" data-timezone="')
                                __M_writer(filters.html_escape(filters.decode.utf8(user_timezone)))
                                __M_writer(u'" data-language="')
                                __M_writer(filters.html_escape(filters.decode.utf8(user_language)))
                                __M_writer(u'"></em>\n')
                            __M_writer(u'                                        </p>\n                                        <p class="override-notice">\n')
                            if section.override is not None:
                                if section.format is not None and section.format == "Exam":
                                    __M_writer(u'                                                    ')
                                    __M_writer(filters.html_escape(filters.decode.utf8(_("Suspicious activity detected during proctored exam review. Exam score 0."))))
                                    __M_writer(u'\n')
                                else:
                                    __M_writer(u'                                                    ')
                                    __M_writer(filters.html_escape(filters.decode.utf8(_("Section grade has been overridden."))))
                                    __M_writer(u'\n')
                            __M_writer(u'                                        </p>\n')
                            if len(section.problem_scores.values()) > 0:
                                if section.show_grades(staff_access):
                                    __M_writer(u'                                          <dl class="scores">\n                                              <dt class="hd hd-6">')
                                    __M_writer(filters.html_escape(filters.decode.utf8( _("Problem Scores: ") if section.graded else _("Practice Scores: "))))
                                    __M_writer(u'</dt>\n')
                                    for score in section.problem_scores.values():
                                        __M_writer(u'                                              <dd>')
                                        __M_writer(filters.html_escape(filters.decode.utf8("{0:.3n}/{1:.3n}".format(float(score.earned),float(score.possible)))))
                                        __M_writer(u'</dd>\n')
                                    __M_writer(u'                                          </dl>\n')
                                else:
                                    __M_writer(u'                                            <p class="hide-scores">\n')
                                    if section.show_correctness == 'past_due':
                                        if section.graded:
                                            __M_writer(u'                                                  ')
                                            __M_writer(filters.html_escape(filters.decode.utf8(_("Problem scores are hidden until the due date."))))
                                            __M_writer(u'\n')
                                        else:
                                            __M_writer(u'                                                  ')
                                            __M_writer(filters.html_escape(filters.decode.utf8(_("Practice scores are hidden until the due date."))))
                                            __M_writer(u'\n')
                                    else:
                                        if section.graded:
                                            __M_writer(u'                                                  ')
                                            __M_writer(filters.html_escape(filters.decode.utf8(_("Problem scores are hidden."))))
                                            __M_writer(u'\n')
                                        else:
                                            __M_writer(u'                                                  ')
                                            __M_writer(filters.html_escape(filters.decode.utf8(_("Practice scores are hidden."))))
                                            __M_writer(u'\n')
                                    __M_writer(u'                                            </p>\n')
                            else:
                                __M_writer(u'                                        <p class="no-scores">')
                                __M_writer(filters.html_escape(filters.decode.utf8(_("No problem scores in this section"))))
                                __M_writer(u'</p>\n')
                            __M_writer(u'                                    </div>\n')
                        __M_writer(u'                            </div>\n                        </section>\n')
            finally:
                loop = __M_loop._exit()
            __M_writer(u'                </section>\n')
        __M_writer(u'            </section>\n        </div>\n    </div>\n</main>\n')
        def ccall(caller):
            def body():
                __M_writer = context.writer()
                __M_writer(u'\n    DateUtilFactory.transform(iterationKey=".localized-datetime");\n')
                return ''
            return [body]
        context.caller_stack.nextcaller = runtime.Namespace('caller', context, callables=ccall(__M_caller))
        try:
            __M_writer(filters.html_escape(filters.decode.utf8(static.require_module_async(class_name=u'DateUtilFactory',module_name=u'js/dateutil_factory'))))
        finally:
            context.caller_stack.nextcaller = None
        __M_writer(u'\n')
        return ''
    finally:
        context.caller_stack._pop_frame()
Example #6
0
 def username(self, obj):
     return HTML('<a href="{}">{}</a>').format(
         reverse("admin:auth_user_change", args=(obj.enrollment.user.id, )),
         obj.enrollment.user.username)
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),
    }

    if not access['staff']:
        raise Http404()

    is_white_label = CourseMode.is_white_label(course_key)

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

    sections = [
        _section_course_info(course, access),
        _section_membership(course, access, is_white_label),
        _section_cohort_management(course, access),
        _section_student_admin(course, access),
        _section_data_download(course, access),
    ]

    analytics_dashboard_message = None
    if settings.ANALYTICS_DASHBOARD_URL:
        # Construct a URL to the external analytics dashboard
        analytics_dashboard_url = '{0}/courses/{1}'.format(
            settings.ANALYTICS_DASHBOARD_URL, unicode(course_key))
        link_start = HTML("<a href=\"{}\" target=\"_blank\">").format(
            analytics_dashboard_url)
        analytics_dashboard_message = _(
            "To gain insights into student enrollment and participation {link_start}"
            "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.",
            unicode(course_key), len(paid_modes))

    if settings.FEATURES.get('INDIVIDUAL_DUE_DATES') and access['instructor']:
        sections.insert(3, _section_extensions(course))

    # Gate access to course email by feature flag & by course-specific authorization
    if BulkEmailFlag.feature_enabled(course_key):
        sections.append(_section_send_email(course, access))

    # Gate access to Metrics tab by featue flag and staff authorization
    if settings.FEATURES['CLASS_DASHBOARD'] and access['staff']:
        sections.append(_section_metrics(course, access))

    # Gate access to Ecommerce tab
    if course_mode_has_price and (access['finance_admin']
                                  or access['sales_admin']):
        sections.append(
            _section_e_commerce(course, access, paid_modes[0], is_white_label,
                                reports_enabled))

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

    # NOTE: For now, if we only have procotred exams enabled, then only platform Staff
    # (user.is_staff) will be able to view the special exams tab. This may
    # change in the future
    can_see_special_exams = (
        ((course.enable_proctored_exams and request.user.is_staff)
         or course.enable_timed_exams)
        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.
    certs_enabled = CertificateGenerationConfiguration.current().enabled
    if certs_enabled and access['admin']:
        sections.append(_section_certificates(course))

    disable_buttons = not _is_small_course(course_key)

    certificate_white_list = CertificateWhitelist.get_certificate_white_list(
        course_key)
    generate_certificate_exceptions_url = reverse(  # pylint: disable=invalid-name
        'generate_certificate_exceptions',
        kwargs={
            'course_id': unicode(course_key),
            'generate_for': ''
        })
    generate_bulk_certificate_exceptions_url = reverse(  # pylint: disable=invalid-name
        'generate_bulk_certificate_exceptions',
        kwargs={'course_id': unicode(course_key)})
    certificate_exception_view_url = reverse(
        'certificate_exception_view',
        kwargs={'course_id': unicode(course_key)})

    certificate_invalidation_view_url = reverse(  # pylint: disable=invalid-name
        'certificate_invalidation_view',
        kwargs={'course_id': unicode(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,
    }

    return render_to_response(
        'instructor/instructor_dashboard_2/instructor_dashboard_2.html',
        context)
Example #8
0
def _handle_failed_authentication(user, authenticated_user):
    """
    Handles updating the failed login count, inactive user notifications, and logging failed authentications.
    """
    if user:
        if LoginFailures.is_feature_enabled():
            LoginFailures.increment_lockout_counter(user)

        if authenticated_user and not user.is_active:
            _log_and_raise_inactive_user_auth_error(user)

        # if we didn't find this username earlier, the account for this email
        # doesn't exist, and doesn't have a corresponding password
        if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
            loggable_id = user.id if user else "<unknown>"
            AUDIT_LOG.warning(
                u"Login failed - password for user.id: {0} is invalid".format(
                    loggable_id))
        else:
            AUDIT_LOG.warning(
                u"Login failed - password for {0} is invalid".format(
                    user.email))

    if user and LoginFailures.is_feature_enabled():
        blocked_threshold, failure_count = LoginFailures.check_user_reset_password_threshold(
            user)
        if blocked_threshold:
            if not LoginFailures.is_user_locked_out(user):
                max_failures_allowed = settings.MAX_FAILED_LOGIN_ATTEMPTS_ALLOWED
                remaining_attempts = max_failures_allowed - failure_count
                if not should_redirect_to_logistration_mircrofrontend:  # pylint: disable=no-else-raise
                    raise AuthFailedError(
                        Text(
                            _('Email or password is incorrect.'
                              '{li_start}You have {remaining_attempts} more sign-in '
                              'attempts before your account is temporarily locked.{li_end}'
                              '{li_start}If you\'ve forgotten your password, click '
                              '{link_start}here{link_end} to reset.{li_end}')).
                        format(link_start=HTML(
                            '<a http="#login" class="form-toggle" data-type="password-reset">'
                        ),
                               link_end=HTML('</a>'),
                               li_start=HTML('<li>'),
                               li_end=HTML('</li>'),
                               remaining_attempts=remaining_attempts))
                else:
                    raise AuthFailedError(
                        Text(
                            _('Email or password is incorrect.\n'
                              'You have {remaining_attempts} more sign-in '
                              'attempts before your account is temporarily locked.\n'
                              'If you{quote}ve forgotten your password, click '
                              '{link_start}here{link_end} to reset.\n')).
                        format(quote=HTML("'"),
                               link_start=HTML('<a href="/reset" >'),
                               link_end=HTML('</a>'),
                               remaining_attempts=remaining_attempts))
            else:
                _generate_locked_out_error_message()

    raise AuthFailedError(_('Email or password is incorrect.'))
Example #9
0
    def test_render_response_xml(self):
        # Generate some XML for a string response
        kwargs = {
            'question_text': "Test question",
            'explanation_text': "Test explanation",
            'answer': 'Test answer',
            'hints': [('test prompt', 'test_hint', 'test hint text')]
        }
        xml_str = StringResponseXMLFactory().build_xml(**kwargs)

        # Mock out the template renderer
        the_system = test_capa_system()
        the_system.render_template = mock.Mock()
        the_system.render_template.return_value = "<div class='input-template-render'>Input Template Render</div>"

        # Create the problem and render the HTML
        problem = new_loncapa_problem(xml_str, capa_system=the_system)
        rendered_html = etree.XML(problem.get_html())
        # Expect problem has been turned into a <div>
        self.assertEqual(rendered_html.tag, "div")

        # Expect that the response has been turned into a <div> with correct attributes
        response_element = rendered_html.find('div')

        self.assertEqual(response_element.tag, "div")
        self.assertEqual(response_element.attrib["aria-label"], "Question 1")

        # Expect that the response div.wrapper-problem-response
        # that contains a <div> for the textline
        textline_element = response_element.find('div')
        self.assertEqual(textline_element.text, 'Input Template Render')

        # Expect a child <div> for the solution
        # with the rendered template
        solution_element = rendered_html.xpath(
            '//div[@class="input-template-render"]')[0]
        self.assertEqual(solution_element.text, 'Input Template Render')

        # Expect that the template renderer was called with the correct
        # arguments, once for the textline input and once for
        # the solution
        expected_textline_context = {
            'STATIC_URL': '/dummy-static/',
            'status': the_system.STATUS_CLASS('unsubmitted'),
            'value': '',
            'preprocessor': None,
            'msg': '',
            'inline': False,
            'hidden': False,
            'do_math': False,
            'id': '1_2_1',
            'trailing_text': '',
            'size': None,
            'response_data': {
                'label': 'Test question',
                'descriptions': {}
            },
            'describedby_html': HTML('aria-describedby="status_1_2_1"')
        }

        expected_solution_context = {'id': '1_solution_1'}

        expected_calls = [
            mock.call('textline.html', expected_textline_context),
            mock.call('solutionspan.html', expected_solution_context),
            mock.call('textline.html', expected_textline_context),
            mock.call('solutionspan.html', expected_solution_context)
        ]

        self.assertEqual(the_system.render_template.call_args_list,
                         expected_calls)
Example #10
0
def _register_course_home_messages(request, course, user_access,
                                   course_start_data):
    """
    Register messages to be shown in the course home content page.
    """
    allow_anonymous = allow_public_access(course, [COURSE_VISIBILITY_PUBLIC])

    if user_access['is_anonymous'] and not allow_anonymous:
        sign_in_or_register_text = (_(
            u'{sign_in_link} or {register_link} and then enroll in this course.'
        ) if not CourseMode.is_masters_only(course.id) else
                                    _(u'{sign_in_link} or {register_link}.'))
        CourseHomeMessages.register_info_message(
            request,
            Text(sign_in_or_register_text).format(
                sign_in_link=HTML(
                    u'<a href="/login?next={current_url}">{sign_in_label}</a>'
                ).format(
                    sign_in_label=_('Sign in'),
                    current_url=urlquote_plus(request.path),
                ),
                register_link=HTML(
                    u'<a href="/register?next={current_url}">{register_label}</a>'
                ).format(
                    register_label=_('register'),
                    current_url=urlquote_plus(request.path),
                )),
            title=Text(
                _('You must be enrolled in the course to see course content.')
            ))
    if not user_access['is_anonymous'] and not user_access['is_staff'] and \
            not user_access['is_enrolled']:

        title = Text(_(u'Welcome to {course_display_name}')).format(
            course_display_name=course.display_name)

        if CourseMode.is_masters_only(course.id):
            # if a course is a Master's only course, we will not offer user ability to self-enroll
            CourseHomeMessages.register_info_message(
                request,
                Text(
                    _('You must be enrolled in the course to see course content. '
                      'Please contact your degree administrator or edX Support if you have questions.'
                      )),
                title=title)
        elif not course.invitation_only:
            CourseHomeMessages.register_info_message(
                request,
                Text(
                    _(u'{open_enroll_link}Enroll now{close_enroll_link} to access the full course.'
                      )).format(open_enroll_link=HTML(
                          '<button class="enroll-btn btn-link">'),
                                close_enroll_link=HTML('</button>')),
                title=title)
        else:
            CourseHomeMessages.register_info_message(
                request,
                Text(
                    _('You must be enrolled in the course to see course content.'
                      )),
            )
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 Ecommerce tab
    if course_mode_has_price and (access['finance_admin']
                                  or access['sales_admin']):
        sections.append(
            _section_e_commerce(course, access, paid_modes[0], is_white_label,
                                reports_enabled))

    # 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 _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 #12
0
def generate_course_expired_fragment(user, course):
    message = generate_course_expired_message(user, course)
    if message:
        return Fragment(HTML(u"""\
            <div class="course-expiration-message">{}</div>
        """).format(message))
Example #13
0
def generate_course_expired_message(user, course):
    """
    Generate the message for the user course expiration date if it exists.
    """
    if not CourseDurationLimitConfig.enabled_for_enrollment(user=user, course_key=course.id):
        return

    expiration_date = get_user_course_expiration_date(user, course)
    if not expiration_date:
        return

    if is_masquerading_as_specific_student(user, course.id) and timezone.now() > expiration_date:
        upgrade_message = _('This learner does not have access to this course. '
                            u'Their access expired on {expiration_date}.')
        return HTML(upgrade_message).format(
            expiration_date=strftime_localized(expiration_date, EXPIRATION_DATE_FORMAT_STR)
        )
    else:
        enrollment = CourseEnrollment.get_enrollment(user, course.id)
        if enrollment is None:
            return

        upgrade_deadline = enrollment.upgrade_deadline
        now = timezone.now()
        course_upgrade_deadline = enrollment.course_upgrade_deadline
        if (not upgrade_deadline) or (upgrade_deadline < now):
            upgrade_deadline = course_upgrade_deadline

        expiration_message = _(u'{strong_open}Audit Access Expires {expiration_date}{strong_close}'
                               u'{line_break}You lose all access to this course, including your progress, on '
                               u'{expiration_date}.')
        upgrade_deadline_message = _(u'{line_break}Upgrade by {upgrade_deadline} to get unlimited access to the course '
                                     u'as long as it exists on the site. {a_open}Upgrade now{sronly_span_open} to '
                                     u'retain access past {expiration_date}{span_close}{a_close}')
        full_message = expiration_message
        if upgrade_deadline and now < upgrade_deadline:
            full_message += upgrade_deadline_message
            using_upgrade_messaging = True
        else:
            using_upgrade_messaging = False

        language = get_language()
        date_string = get_date_string()
        formatted_expiration_date = date_string.format(
            language=language,
            formatted_date=expiration_date.strftime("%Y-%m-%d"),
            formatted_date_localized=strftime_localized(expiration_date, EXPIRATION_DATE_FORMAT_STR)
        )
        if using_upgrade_messaging:
            formatted_upgrade_deadline = date_string.format(
                language=language,
                formatted_date=upgrade_deadline.strftime("%Y-%m-%d"),
                formatted_date_localized=strftime_localized(upgrade_deadline, EXPIRATION_DATE_FORMAT_STR)
            )

            return HTML(full_message).format(
                a_open=HTML(u'<a href="{upgrade_link}">').format(
                    upgrade_link=verified_upgrade_deadline_link(user=user, course=course)
                ),
                sronly_span_open=HTML('<span class="sr-only">'),
                span_close=HTML('</span>'),
                a_close=HTML('</a>'),
                expiration_date=HTML(formatted_expiration_date),
                strong_open=HTML('<strong>'),
                strong_close=HTML('</strong>'),
                line_break=HTML('<br>'),
                upgrade_deadline=HTML(formatted_upgrade_deadline)
            )

        else:
            return HTML(full_message).format(
                span_close=HTML('</span>'),
                expiration_date=HTML(formatted_expiration_date),
                strong_open=HTML('<strong>'),
                strong_close=HTML('</strong>'),
                line_break=HTML('<br>'),
            )
Example #14
0
def password_reset_confirm_wrapper(request, uidb36=None, token=None):
    """
    A wrapper around django.contrib.auth.views.password_reset_confirm.
    Needed because we want to set the user as active at this step.
    We also optionally do some additional password policy checks.
    """
    # convert old-style base36-encoded user id to base64
    uidb64 = uidb36_to_uidb64(uidb36)
    platform_name = {
        "platform_name": configuration_helpers.get_value('platform_name', settings.PLATFORM_NAME)
    }

    # User can not get this link unless account recovery feature is enabled.
    if 'is_account_recovery' in request.GET and not is_secondary_email_feature_enabled():
        raise Http404

    try:
        uid_int = base36_to_int(uidb36)
        user = User.objects.get(id=uid_int)
    except (ValueError, User.DoesNotExist):
        # if there's any error getting a user, just let django's
        # password_reset_confirm function handle it.
        return password_reset_confirm(
            request, uidb64=uidb64, token=token, extra_context=platform_name
        )

    if UserRetirementRequest.has_user_requested_retirement(user):
        # Refuse to reset the password of any user that has requested retirement.
        context = {
            'validlink': True,
            'form': None,
            'title': _('Password reset unsuccessful'),
            'err_msg': _('Error in resetting your password.'),
        }
        context.update(platform_name)
        return TemplateResponse(
            request, 'registration/password_reset_confirm.html', context
        )

    if waffle().is_enabled(PREVENT_AUTH_USER_WRITES):
        context = {
            'validlink': False,
            'form': None,
            'title': _('Password reset unsuccessful'),
            'err_msg': SYSTEM_MAINTENANCE_MSG,
        }
        context.update(platform_name)
        return TemplateResponse(
            request, 'registration/password_reset_confirm.html', context
        )

    if request.method == 'POST':
        # We have to make a copy of request.POST because it is a QueryDict object which is immutable until copied.
        # We have to use request.POST because the password_reset_confirm method takes in the request and a user's
        # password is set to the request.POST['new_password1'] field. We have to also normalize the new_password2
        # field so it passes the equivalence check that new_password1 == new_password2
        # In order to switch out of having to do this copy, we would want to move the normalize_password code into
        # a custom User model's set_password method to ensure it is always happening upon calling set_password.
        request.POST = request.POST.copy()
        request.POST['new_password1'] = normalize_password(request.POST['new_password1'])
        request.POST['new_password2'] = normalize_password(request.POST['new_password2'])

        password = request.POST['new_password1']

        try:
            validate_password(password, user=user)
        except ValidationError as err:
            # We have a password reset attempt which violates some security
            # policy, or any other validation. Use the existing Django template to communicate that
            # back to the user.
            context = {
                'validlink': True,
                'form': None,
                'title': _('Password reset unsuccessful'),
                'err_msg': ' '.join(err.messages),
            }
            context.update(platform_name)
            return TemplateResponse(
                request, 'registration/password_reset_confirm.html', context
            )

        # remember what the old password hash is before we call down
        old_password_hash = user.password

        if 'is_account_recovery' in request.GET:
            response = password_reset_confirm(
                request,
                uidb64=uidb64,
                token=token,
                extra_context=platform_name,
                template_name='registration/password_reset_confirm.html',
                post_reset_redirect='signin_user',
            )
        else:
            response = password_reset_confirm(
                request, uidb64=uidb64, token=token, extra_context=platform_name
            )

        # If password reset was unsuccessful a template response is returned (status_code 200).
        # Check if form is invalid then show an error to the user.
        # Note if password reset was successful we get response redirect (status_code 302).
        if response.status_code == 200:
            form_valid = response.context_data['form'].is_valid() if response.context_data['form'] else False
            if not form_valid:
                log.warning(
                    u'Unable to reset password for user [%s] because form is not valid. '
                    u'A possible cause is that the user had an invalid reset token',
                    user.username,
                )
                response.context_data['err_msg'] = _('Error in resetting your password. Please try again.')
                return response

        # get the updated user
        updated_user = User.objects.get(id=uid_int)
        if 'is_account_recovery' in request.GET:
            try:
                updated_user.email = updated_user.account_recovery.secondary_email
                updated_user.account_recovery.delete()
                # emit an event that the user changed their secondary email to the primary email
                tracker.emit(
                    SETTING_CHANGE_INITIATED,
                    {
                        "setting": "email",
                        "old": user.email,
                        "new": updated_user.email,
                        "user_id": updated_user.id,
                    }
                )
            except ObjectDoesNotExist:
                log.error(
                    'Account recovery process initiated without AccountRecovery instance for user {username}'.format(
                        username=updated_user.username
                    )
                )

        updated_user.save()

        if response.status_code == 302 and 'is_account_recovery' in request.GET:
            messages.success(
                request,
                HTML(_(
                    '{html_start}Password Creation Complete{html_end}'
                    'Your password has been created. {bold_start}{email}{bold_end} is now your primary login email.'
                )).format(
                    support_url=configuration_helpers.get_value('SUPPORT_SITE_LINK', settings.SUPPORT_SITE_LINK),
                    html_start=HTML('<p class="message-title">'),
                    html_end=HTML('</p>'),
                    bold_start=HTML('<b>'),
                    bold_end=HTML('</b>'),
                    email=updated_user.email,
                ),
                extra_tags='account-recovery aa-icon submission-success'
            )
    else:
        response = password_reset_confirm(
            request, uidb64=uidb64, token=token, extra_context=platform_name
        )

        response_was_successful = response.context_data.get('validlink')
        if response_was_successful and not user.is_active:
            user.is_active = True
            user.save()

    return response
Example #15
0
def student_dashboard(request):  # lint-amnesty, pylint: disable=too-many-statements
    """
    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.

    """
    landing_page = request.POST.get(
        'https://rc360service-q6uoj7vcrq-uc.a.run.app/r1/landingpage/getLandingPageDetails',
        '')
    # return render_to_response(landing_page)
    # decrypt_message(landing_page)
    #    print(landing_page,"")
    user = request.user
    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)  # lint-amnesty, pylint: disable=unused-variable
        except PendingSecondaryEmailChange.DoesNotExist:
            try:
                account_recovery_obj = AccountRecovery.objects.get(user=user)  # lint-amnesty, pylint: disable=unused-variable
            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 = LegacyWaffleFlag(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)

    if 'notlive' in request.GET:
        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
        ]

#   landing_page =request.POST['https://rc360service-q6uoj7vcrq-uc.a.run.app/r1/landingpage/getLandingPageDetails']
    context = {
        'urls':
        urls,
        'landing_page':
        landing_page,
        '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 #16
0
class LTIFields:
    """
    Fields to define and obtain LTI tool from provider are set here,
    except credentials, which should be set in course settings::

    `lti_id` is id to connect tool with credentials in course settings. It should not contain :: (double semicolon)
    `launch_url` is launch URL of tool.
    `custom_parameters` are additional parameters to navigate to proper book and book page.

    For example, for Vitalsource provider, `launch_url` should be
    *https://bc-staging.vitalsource.com/books/book*,
    and to get to proper book and book page, you should set custom parameters as::

        vbid=put_book_id_here
        book_location=page/put_page_number_here

    Default non-empty URL for `launch_url` is needed due to oauthlib demand (URL scheme should be presented)::

    https://github.com/idan/oauthlib/blob/master/oauthlib/oauth1/rfc5849/signature.py#L136
    """
    display_name = String(
        display_name=_("Display Name"),
        help=_(
            "The display name for this component. "
            "Analytics reports may also use the display name to identify this component."
        ),
        scope=Scope.settings,
        default="LTI",
    )
    lti_id = String(
        display_name=_("LTI ID"),
        help=Text(_(
            "Enter the LTI ID for the external LTI provider.  "
            "This value must be the same LTI ID that you entered in the "
            "LTI Passports setting on the Advanced Settings page."
            "{break_tag}See {docs_anchor_open}the edX LTI documentation{anchor_close} for more details on this setting."
        )).format(
            break_tag=HTML(BREAK_TAG),
            docs_anchor_open=HTML(DOCS_ANCHOR_TAG_OPEN),
            anchor_close=HTML("</a>")
        ),
        default='',
        scope=Scope.settings
    )
    launch_url = String(
        display_name=_("LTI URL"),
        help=Text(_(
            "Enter the URL of the external tool that this component launches. "
            "This setting is only used when Hide External Tool is set to False."
            "{break_tag}See {docs_anchor_open}the edX LTI documentation{anchor_close} for more details on this setting."
        )).format(
            break_tag=HTML(BREAK_TAG),
            docs_anchor_open=HTML(DOCS_ANCHOR_TAG_OPEN),
            anchor_close=HTML("</a>")
        ),
        default='http://www.example.com',
        scope=Scope.settings)
    custom_parameters = List(
        display_name=_("Custom Parameters"),
        help=Text(_(
            "Add the key/value pair for any custom parameters, such as the page your e-book should open to or "
            "the background color for this component."
            "{break_tag}See {docs_anchor_open}the edX LTI documentation{anchor_close} for more details on this setting."
        )).format(
            break_tag=HTML(BREAK_TAG),
            docs_anchor_open=HTML(DOCS_ANCHOR_TAG_OPEN),
            anchor_close=HTML("</a>")
        ),
        scope=Scope.settings)
    open_in_a_new_page = Boolean(
        display_name=_("Open in New Page"),
        help=_(
            "Select True if you want students to click a link that opens the LTI tool in a new window. "
            "Select False if you want the LTI content to open in an IFrame in the current page. "
            "This setting is only used when Hide External Tool is set to False.  "
        ),
        default=True,
        scope=Scope.settings
    )
    has_score = Boolean(
        display_name=_("Scored"),
        help=_(
            "Select True if this component will receive a numerical score from the external LTI system."
        ),
        default=False,
        scope=Scope.settings
    )
    weight = Float(
        display_name=_("Weight"),
        help=_(
            "Enter the number of points possible for this component.  "
            "The default value is 1.0.  "
            "This setting is only used when Scored is set to True."
        ),
        default=1.0,
        scope=Scope.settings,
        values={"min": 0},
    )
    module_score = Float(
        help=_("The score kept in the xblock KVS -- duplicate of the published score in django DB"),
        default=None,
        scope=Scope.user_state
    )
    score_comment = String(
        help=_("Comment as returned from grader, LTI2.0 spec"),
        default="",
        scope=Scope.user_state
    )
    hide_launch = Boolean(
        display_name=_("Hide External Tool"),
        help=_(
            "Select True if you want to use this component as a placeholder for syncing with an external grading  "
            "system rather than launch an external tool.  "
            "This setting hides the Launch button and any IFrames for this component."
        ),
        default=False,
        scope=Scope.settings
    )

    # Users will be presented with a message indicating that their e-mail/username would be sent to a third
    # party application. When "Open in New Page" is not selected, the tool automatically appears without any user action.  # lint-amnesty, pylint: disable=line-too-long
    ask_to_send_username = Boolean(
        display_name=_("Request user's username"),
        # Translators: This is used to request the user's username for a third party service.
        help=_("Select True to request the user's username."),
        default=False,
        scope=Scope.settings
    )
    ask_to_send_email = Boolean(
        display_name=_("Request user's email"),
        # Translators: This is used to request the user's email for a third party service.
        help=_("Select True to request the user's email address."),
        default=False,
        scope=Scope.settings
    )

    description = String(
        display_name=_("LTI Application Information"),
        help=_(
            "Enter a description of the third party application. If requesting username and/or email, use this text box to inform users "  # lint-amnesty, pylint: disable=line-too-long
            "why their username and/or email will be forwarded to a third party application."
        ),
        default="",
        scope=Scope.settings
    )

    button_text = String(
        display_name=_("Button Text"),
        help=_(
            "Enter the text on the button used to launch the third party application."
        ),
        default="",
        scope=Scope.settings
    )

    accept_grades_past_due = Boolean(
        display_name=_("Accept grades past deadline"),
        help=_("Select True to allow third party systems to post grades past the deadline."),
        default=True,
        scope=Scope.settings
    )
class UserMessagesTestCase(TestCase):
    """
    Unit tests for page level user messages.
    """
    def setUp(self):
        super(UserMessagesTestCase, self).setUp()
        self.student = UserFactory.create()
        self.request = RequestFactory().request()
        self.request.session = {}
        self.request.user = self.student
        MessageMiddleware().process_request(self.request)

    @ddt.data(
        ('Rock & Roll', '<div class="message-content">Rock &amp; Roll</div>'),
        (Text('Rock & Roll'),
         '<div class="message-content">Rock &amp; Roll</div>'),
        (HTML('<p>Hello, world!</p>'),
         '<div class="message-content"><p>Hello, world!</p></div>'))
    @ddt.unpack
    def test_message_escaping(self, message, expected_message_html):
        """
        Verifies that a user message is escaped correctly.
        """
        PageLevelMessages.register_user_message(self.request,
                                                UserMessageType.INFO, message)
        messages = list(PageLevelMessages.user_messages(self.request))
        self.assertEqual(len(messages), 1)
        self.assertEquals(messages[0].message_html, expected_message_html)

    @ddt.data(
        (UserMessageType.ERROR, 'alert-danger', 'fa fa-warning'),
        (UserMessageType.INFO, 'alert-info', 'fa fa-bullhorn'),
        (UserMessageType.SUCCESS, 'alert-success', 'fa fa-check-circle'),
        (UserMessageType.WARNING, 'alert-warning', 'fa fa-warning'),
    )
    @ddt.unpack
    def test_message_icon(self, message_type, expected_css_class,
                          expected_icon_class):
        """
        Verifies that a user message returns the correct CSS and icon classes.
        """
        PageLevelMessages.register_user_message(self.request, message_type,
                                                TEST_MESSAGE)
        messages = list(PageLevelMessages.user_messages(self.request))
        self.assertEqual(len(messages), 1)
        self.assertEquals(messages[0].css_class, expected_css_class)
        self.assertEquals(messages[0].icon_class, expected_icon_class)

    @ddt.data(
        (normalize_repr(
            PageLevelMessages.register_error_message), UserMessageType.ERROR),
        (normalize_repr(
            PageLevelMessages.register_info_message), UserMessageType.INFO),
        (normalize_repr(PageLevelMessages.register_success_message),
         UserMessageType.SUCCESS),
        (normalize_repr(PageLevelMessages.register_warning_message),
         UserMessageType.WARNING),
    )
    @ddt.unpack
    def test_message_type(self, register_message_function,
                          expected_message_type):
        """
        Verifies that each user message function returns the correct type.
        """
        register_message_function(self.request, TEST_MESSAGE)
        messages = list(PageLevelMessages.user_messages(self.request))
        self.assertEqual(len(messages), 1)
        self.assertEquals(messages[0].type, expected_message_type)
Example #18
0
def _format_discounted_price(original_price, discount_price):
    """Helper method that returns HTML containing a strikeout price with discount."""
    # Separate out this string because it has a lot of syntax but no actual information for
    # translators to translate
    formatted_discount_price = HTML(
        '{s_dp}{discount_price}{e_p} {s_st}{s_op}{original_price}{e_p}{e_st}'
    ).format(
        original_price=original_price,
        discount_price=discount_price,
        s_op=HTML("<span class='price original'>"),
        s_dp=HTML("<span class='price discount'>"),
        s_st=HTML("<del aria-hidden='true'>"),
        e_p=HTML('</span>'),
        e_st=HTML('</del>'),
    )

    return (
        HTML(_(
            '{s_sr}Original price: {s_op}{original_price}{e_p}, discount price: {e_sr}{formatted_discount_price}'
        )).format(
            original_price=original_price,
            formatted_discount_price=formatted_discount_price,
            s_sr=HTML("<span class='sr-only'>"),
            s_op=HTML("<span class='price original'>"),
            e_p=HTML('</span>'),
            e_sr=HTML('</span>'),
        )
    )
Example #19
0
 def course_id(self, obj):
     return HTML('<a href="{}">{}</a>').format(
         reverse("admin:course_overviews_courseoverview_change",
                 args=(obj.enrollment.course_id, )),
         obj.enrollment.course_id)
Example #20
0
 def __unicode__(self):
     keys = [field.name for field in self._meta.get_fields() if field.name not in ('created', 'modified')]
     return HTML(u'{}<{!r}').format(
         HTML(self.__class__.__name__),
         {key: HTML(getattr(self, key)) for key in keys}
     )
Example #21
0
 def __str__(self):  # lint-amnesty, pylint: disable=invalid-str-returned
     keys = [field.name for field in self._meta.get_fields() if field.name not in ('created', 'modified')]
     return HTML(u'{}<{!r}').format(
         HTML(self.__class__.__name__),
         {key: HTML(getattr(self, key)) for key in keys}
     )
Example #22
0
def render_body(context, **pageargs):
    __M_caller = context.caller_stack._push_frame()
    try:
        __M_locals = __M_dict_builtin(pageargs=pageargs)

        def requirejs():
            return render_requirejs(context._locals(__M_locals))

        request = context.get('request', UNDEFINED)
        EDX_ROOT_URL = context.get('EDX_ROOT_URL', UNDEFINED)
        static = _mako_get_namespace(context, 'static')
        LANGUAGE_CODE = context.get('LANGUAGE_CODE', UNDEFINED)

        def bodyclass():
            return render_bodyclass(context._locals(__M_locals))

        def title():
            return render_title(context._locals(__M_locals))

        context_library = context.get('context_library', UNDEFINED)

        def jsextra():
            return render_jsextra(context._locals(__M_locals))

        def content():
            return render_content(context._locals(__M_locals))

        getattr = context.get('getattr', UNDEFINED)
        uses_bootstrap = context.get('uses_bootstrap', UNDEFINED)

        def view_notes():
            return render_view_notes(context._locals(__M_locals))

        user = context.get('user', UNDEFINED)
        hasattr = context.get('hasattr', UNDEFINED)
        context_course = context.get('context_course', UNDEFINED)
        settings = context.get('settings', UNDEFINED)
        list = context.get('list', UNDEFINED)

        def page_bundle():
            return render_page_bundle(context._locals(__M_locals))

        def page_alert():
            return render_page_alert(context._locals(__M_locals))

        def header_extras():
            return render_header_extras(context._locals(__M_locals))

        def modal_placeholder():
            return render_modal_placeholder(context._locals(__M_locals))

        self = context.get('self', UNDEFINED)
        __M_writer = context.writer()
        __M_writer(u'\n')
        __M_writer(u'\n\n')
        __M_writer(u'\n')
        __M_writer(u'\n\n')
        __M_writer(
            u'\n<!doctype html>\n<!--[if lte IE 9]><html class="ie9 lte9" lang="'
        )
        __M_writer(filters.html_escape(filters.decode.utf8(LANGUAGE_CODE)))
        __M_writer(u'"><![endif]-->\n<!--[if !IE]><<!--><html lang="')
        __M_writer(filters.html_escape(filters.decode.utf8(LANGUAGE_CODE)))
        __M_writer(u'"><!--<![endif]-->\n  <head dir="')
        __M_writer(filters.html_escape(filters.decode.utf8(static.dir_rtl())))
        __M_writer(
            u'">\n    <meta charset="utf-8">\n    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">\n    <meta name="openedx-release-line" content="'
        )
        __M_writer(filters.html_escape(filters.decode.utf8(RELEASE_LINE)))
        __M_writer(u'" />\n    <title>\n        ')
        if 'parent' not in context._data or not hasattr(
                context._data['parent'], 'title'):
            context['self'].title(**pageargs)

        __M_writer(u' |\n')
        if context_course:
            __M_writer(u'        ')
            ctx_loc = context_course.location

            __M_locals_builtin_stored = __M_locals_builtin()
            __M_locals.update(
                __M_dict_builtin([(__M_key, __M_locals_builtin_stored[__M_key])
                                  for __M_key in ['ctx_loc']
                                  if __M_key in __M_locals_builtin_stored]))
            __M_writer(u'\n        ')
            __M_writer(
                filters.html_escape(
                    filters.decode.utf8(
                        context_course.display_name_with_default)))
            __M_writer(u' |\n')
        elif context_library:
            __M_writer(u'        ')
            __M_writer(
                filters.html_escape(
                    filters.decode.utf8(
                        context_library.display_name_with_default)))
            __M_writer(u' |\n')
        __M_writer(u'        ')
        __M_writer(
            filters.html_escape(filters.decode.utf8(settings.STUDIO_NAME)))
        __M_writer(u'\n    </title>\n\n    ')

        jsi18n_path = "js/i18n/{language}/djangojs.js".format(
            language=LANGUAGE_CODE)

        __M_locals_builtin_stored = __M_locals_builtin()
        __M_locals.update(
            __M_dict_builtin([(__M_key, __M_locals_builtin_stored[__M_key])
                              for __M_key in ['jsi18n_path']
                              if __M_key in __M_locals_builtin_stored]))
        __M_writer(u'\n\n')
        if getattr(settings, 'CAPTURE_CONSOLE_LOG', False):
            __M_writer(
                u'        <script type="text/javascript">\n            var oldOnError = window.onerror;\n            window.localStorage.setItem(\'console_log_capture\', JSON.stringify([]));\n\n            window.onerror = function (message, url, lineno, colno, error) {\n                if (oldOnError) {\n                    oldOnError.apply(this, arguments);\n                }\n\n                var messages = JSON.parse(window.localStorage.getItem(\'console_log_capture\'));\n                messages.push([message, url, lineno, colno, (error || {}).stack]);\n                window.localStorage.setItem(\'console_log_capture\', JSON.stringify(messages));\n            }\n        </script>\n'
            )
        __M_writer(u'\n    <script type="text/javascript" src="')
        __M_writer(
            filters.html_escape(filters.decode.utf8(static.url(jsi18n_path))))
        __M_writer(
            u'"></script>\n    <meta name="viewport" content="width=device-width,initial-scale=1">\n    <meta name="path_prefix" content="'
        )
        __M_writer(filters.html_escape(filters.decode.utf8(EDX_ROOT_URL)))
        __M_writer(u'">\n\n    ')

        def ccall(caller):
            def body():
                __M_writer = context.writer()
                return ''

            return [body]

        context.caller_stack.nextcaller = runtime.Namespace(
            'caller', context, callables=ccall(__M_caller))
        try:
            __M_writer(
                filters.html_escape(
                    filters.decode.utf8(static.css(group=u'style-vendor'))))
        finally:
            context.caller_stack.nextcaller = None
        __M_writer(u'\n    ')

        def ccall(caller):
            def body():
                __M_writer = context.writer()
                return ''

            return [body]

        context.caller_stack.nextcaller = runtime.Namespace(
            'caller', context, callables=ccall(__M_caller))
        try:
            __M_writer(
                filters.html_escape(
                    filters.decode.utf8(
                        static.css(group=u'style-vendor-tinymce-content'))))
        finally:
            context.caller_stack.nextcaller = None
        __M_writer(u'\n    ')

        def ccall(caller):
            def body():
                __M_writer = context.writer()
                return ''

            return [body]

        context.caller_stack.nextcaller = runtime.Namespace(
            'caller', context, callables=ccall(__M_caller))
        try:
            __M_writer(
                filters.html_escape(
                    filters.decode.utf8(
                        static.css(group=u'style-vendor-tinymce-skin'))))
        finally:
            context.caller_stack.nextcaller = None
        __M_writer(u'\n\n')
        if uses_bootstrap:
            __M_writer(u'      <link rel="stylesheet" href="')
            __M_writer(
                filters.html_escape(
                    filters.decode.utf8(static.url(self.attr.main_css))))
            __M_writer(u'" type="text/css" media="all" />\n')
        else:
            __M_writer(u'      ')

            def ccall(caller):
                def body():
                    __M_writer = context.writer()
                    return ''

                return [body]

            context.caller_stack.nextcaller = runtime.Namespace(
                'caller', context, callables=ccall(__M_caller))
            try:
                __M_writer(
                    filters.html_escape(
                        filters.decode.utf8(
                            static.css(group=(self.attr.main_css)))))
            finally:
                context.caller_stack.nextcaller = None
            __M_writer(u'\n')
        __M_writer(u'\n    ')
        runtime._include_file(context, u'widgets/segment-io.html',
                              _template_uri)
        __M_writer(u'\n\n    ')
        if 'parent' not in context._data or not hasattr(
                context._data['parent'], 'header_extras'):
            context['self'].header_extras(**pageargs)

        __M_writer(u'\n  </head>\n\n  <body class="')
        __M_writer(filters.html_escape(filters.decode.utf8(static.dir_rtl())))
        __M_writer(u' ')
        if 'parent' not in context._data or not hasattr(
                context._data['parent'], 'bodyclass'):
            context['self'].bodyclass(**pageargs)

        __M_writer(u' lang_')
        __M_writer(filters.html_escape(filters.decode.utf8(LANGUAGE_CODE)))
        __M_writer(u'">\n    ')
        if 'parent' not in context._data or not hasattr(
                context._data['parent'], 'view_notes'):
            context['self'].view_notes(**pageargs)

        __M_writer(u'\n\n    <a class="nav-skip" href="#main">')
        __M_writer(
            filters.html_escape(filters.decode.utf8(
                _("Skip to main content"))))
        __M_writer(u'</a>\n\n    ')

        def ccall(caller):
            def body():
                __M_writer = context.writer()
                return ''

            return [body]

        context.caller_stack.nextcaller = runtime.Namespace(
            'caller', context, callables=ccall(__M_caller))
        try:
            __M_writer(
                filters.html_escape(
                    filters.decode.utf8(static.js(group=u'base_vendor'))))
        finally:
            context.caller_stack.nextcaller = None
        __M_writer(u'\n\n    ')

        def ccall(caller):
            def body():
                __M_writer = context.writer()
                return ''

            return [body]

        context.caller_stack.nextcaller = runtime.Namespace(
            'caller', context, callables=ccall(__M_caller))
        try:
            __M_writer(
                filters.html_escape(
                    filters.decode.utf8(static.webpack(entry=u'commons'))))
        finally:
            context.caller_stack.nextcaller = None
        __M_writer(
            u'\n\n    <script type="text/javascript">\n      window.baseUrl = "'
        )
        __M_writer(js_escaped_string(settings.STATIC_URL))
        __M_writer(
            u'";\n      require.config({\n          baseUrl: window.baseUrl\n      });\n    </script>\n\n    <script type="text/javascript" src="'
        )
        __M_writer(
            filters.html_escape(
                filters.decode.utf8(static.url("cms/js/require-config.js"))))
        __M_writer(
            u'"></script>\n\n    <!-- view -->\n    <div class="wrapper wrapper-view" dir="'
        )
        __M_writer(filters.html_escape(filters.decode.utf8(static.dir_rtl())))
        __M_writer(u'">\n      ')
        online_help_token = self.online_help_token() if hasattr(
            self, 'online_help_token') else None

        __M_locals_builtin_stored = __M_locals_builtin()
        __M_locals.update(
            __M_dict_builtin([(__M_key, __M_locals_builtin_stored[__M_key])
                              for __M_key in ['online_help_token']
                              if __M_key in __M_locals_builtin_stored]))
        __M_writer(u'\n      ')
        runtime._include_file(context,
                              u'widgets/header.html',
                              _template_uri,
                              online_help_token=online_help_token)
        __M_writer(u'\n\n      ')

        banner_messages = list(PageLevelMessages.user_messages(request))

        __M_locals_builtin_stored = __M_locals_builtin()
        __M_locals.update(
            __M_dict_builtin([(__M_key, __M_locals_builtin_stored[__M_key])
                              for __M_key in ['banner_messages']
                              if __M_key in __M_locals_builtin_stored]))
        __M_writer(u'\n\n')
        if banner_messages:
            __M_writer(
                u'        <div class="page-banner">\n          <div class="user-messages">\n'
            )
            for message in banner_messages:
                __M_writer(u'              <div class="alert ')
                __M_writer(
                    filters.html_escape(filters.decode.utf8(
                        message.css_class)))
                __M_writer(
                    u'" role="alert">\n                <span class="icon icon-alert fa '
                )
                __M_writer(
                    filters.html_escape(filters.decode.utf8(
                        message.icon_class)))
                __M_writer(u'" aria-hidden="true"></span>\n                ')
                __M_writer(
                    filters.html_escape(
                        filters.decode.utf8(HTML(message.message_html))))
                __M_writer(u'\n              </div>\n')
            __M_writer(u'          </div>\n        </div>\n')
        __M_writer(u'\n      <div id="page-alert">\n      ')
        if 'parent' not in context._data or not hasattr(
                context._data['parent'], 'page_alert'):
            context['self'].page_alert(**pageargs)

        __M_writer(
            u'\n      </div>\n\n      <main id="main" aria-label="Content" tabindex="-1">\n        <div id="content">\n        '
        )
        if 'parent' not in context._data or not hasattr(
                context._data['parent'], 'content'):
            context['self'].content(**pageargs)

        __M_writer(u'\n        </div>\n      </main>\n\n')
        if user.is_authenticated:
            __M_writer(u'        ')
            runtime._include_file(context,
                                  u'widgets/sock.html',
                                  _template_uri,
                                  online_help_token=online_help_token)
            __M_writer(u'\n')
        __M_writer(u'      ')
        runtime._include_file(context, u'widgets/footer.html', _template_uri)
        __M_writer(
            u'\n\n      <div id="page-notification"></div>\n    </div>\n\n    <div id="page-prompt"></div>\n\n    '
        )
        if 'parent' not in context._data or not hasattr(
                context._data['parent'], 'modal_placeholder'):
            context['self'].modal_placeholder(**pageargs)

        __M_writer(u'\n\n    ')
        if 'parent' not in context._data or not hasattr(
                context._data['parent'], 'jsextra'):
            context['self'].jsextra(**pageargs)

        __M_writer(u'\n\n')
        if context_course:
            __M_writer(u'      ')

            def ccall(caller):
                def body():
                    __M_writer = context.writer()
                    return ''

                return [body]

            context.caller_stack.nextcaller = runtime.Namespace(
                'caller', context, callables=ccall(__M_caller))
            try:
                __M_writer(
                    filters.html_escape(
                        filters.decode.utf8(
                            static.webpack(
                                entry=u'js/factories/context_course'))))
            finally:
                context.caller_stack.nextcaller = None
            __M_writer(
                u'\n      <script type="text/javascript">\n        window.course = new ContextCourse({\n          id: "'
            )
            __M_writer(js_escaped_string(context_course.id))
            __M_writer(u'",\n          name: "')
            __M_writer(
                js_escaped_string(context_course.display_name_with_default))
            __M_writer(u'",\n          url_name: "')
            __M_writer(js_escaped_string(context_course.location.block_id))
            __M_writer(u'",\n          org: "')
            __M_writer(js_escaped_string(context_course.location.org))
            __M_writer(u'",\n          num: "')
            __M_writer(js_escaped_string(context_course.location.course))
            __M_writer(u'",\n          display_course_number: "')
            __M_writer(js_escaped_string(context_course.display_coursenumber))
            __M_writer(u'",\n          revision: "')
            __M_writer(js_escaped_string(context_course.location.branch))
            __M_writer(u'",\n          self_paced: ')
            __M_writer(dump_js_escaped_json(context_course.self_paced))
            __M_writer(u'\n        });\n      </script>\n')
        if user.is_authenticated:
            __M_writer(u'      ')

            def ccall(caller):
                def body():
                    __M_writer = context.writer()
                    return ''

                return [body]

            context.caller_stack.nextcaller = runtime.Namespace(
                'caller', context, callables=ccall(__M_caller))
            try:
                __M_writer(
                    filters.html_escape(
                        filters.decode.utf8(static.webpack(entry=u'js/sock'))))
            finally:
                context.caller_stack.nextcaller = None
            __M_writer(u'\n')
        __M_writer(u'    ')
        if 'parent' not in context._data or not hasattr(
                context._data['parent'], 'page_bundle'):
            context['self'].page_bundle(**pageargs)

        __M_writer(u'\n    ')
        runtime._include_file(context, u'widgets/segment-io-footer.html',
                              _template_uri)
        __M_writer(
            u'\n    <div class="modal-cover"></div>\n  </body>\n</html>\n')
        return ''
    finally:
        context.caller_stack._pop_frame()
Example #23
0
def format_strikeout_price(user,
                           course,
                           base_price=None,
                           check_for_discount=True):
    """
    Return a formatted price, including a struck-out original price if a discount applies, and also
        whether a discount was applied, as the tuple (formatted_price, has_discount).
    """
    if base_price is None:
        base_price = get_course_prices(course, verified_only=True)[0]

    original_price = format_course_price(base_price)

    if not check_for_discount or can_receive_discount(user, course):
        discount_price = base_price * (
            (100.0 - discount_percentage(course)) / 100)
        if discount_price == int(discount_price):
            discount_price = format_course_price(
                "{:0.0f}".format(discount_price))
        else:
            discount_price = format_course_price(
                "{:0.2f}".format(discount_price))

        # Separate out this string because it has a lot of syntax but no actual information for
        # translators to translate
        formatted_discount_price = HTML(
            u"{s_dp}{discount_price}{e_p} {s_st}{s_op}{original_price}{e_p}{e_st}"
        ).format(
            original_price=original_price,
            discount_price=discount_price,
            s_op=HTML("<span class='price original'>"),
            s_dp=HTML("<span class='price discount'>"),
            s_st=HTML("<del aria-hidden='true'>"),
            e_p=HTML("</span>"),
            e_st=HTML("</del>"),
        )

        return (HTML(
            _(u"{s_sr}Original price: {s_op}{original_price}{e_p}, discount price: {e_sr}{formatted_discount_price}"
              )).format(
                  original_price=original_price,
                  formatted_discount_price=formatted_discount_price,
                  s_sr=HTML("<span class='sr-only'>"),
                  s_op=HTML("<span class='price original'>"),
                  e_p=HTML("</span>"),
                  e_sr=HTML("</span>"),
              ), True)
    else:
        return (HTML(u"<span class='price'>{}</span>").format(original_price),
                False)
Example #24
0
 def test_ungettext(self):
     for i in [1, 2]:
         out = Text(ungettext("1 & {}", "2 & {}", i)).format(HTML("<>"))
         self.assertEqual(out, "{} &amp; <>".format(i))
Example #25
0
class TestCourseSockView(SharedModuleStoreTestCase):
    """
    Tests for the course verification sock fragment view.
    """
    @classmethod
    def setUpClass(cls):
        super(TestCourseSockView, cls).setUpClass()

        # Create four courses
        cls.standard_course = CourseFactory.create()
        cls.verified_course = CourseFactory.create()
        cls.verified_course_update_expired = CourseFactory.create()
        cls.verified_course_already_enrolled = CourseFactory.create()

        # Assign each verifiable course an upgrade deadline
        add_course_mode(cls.verified_course, upgrade_deadline_expired=False)
        add_course_mode(cls.verified_course_update_expired,
                        upgrade_deadline_expired=True)
        add_course_mode(cls.verified_course_already_enrolled,
                        upgrade_deadline_expired=False)

    def setUp(self):
        super(TestCourseSockView, self).setUp()
        self.user = UserFactory.create()

        # Enroll the user in the four courses
        CourseEnrollmentFactory.create(user=self.user,
                                       course_id=self.standard_course.id)
        CourseEnrollmentFactory.create(user=self.user,
                                       course_id=self.verified_course.id)
        CourseEnrollmentFactory.create(
            user=self.user, course_id=self.verified_course_update_expired.id)
        CourseEnrollmentFactory.create(
            user=self.user,
            course_id=self.verified_course_already_enrolled.id,
            mode=CourseMode.VERIFIED)

        CommerceConfiguration.objects.create(
            enabled=True, checkout_on_ecommerce_service=True)

        # Log the user in
        self.client.login(username=self.user.username, password=TEST_PASSWORD)

    @override_waffle_flag(DISPLAY_COURSE_SOCK_FLAG, active=True)
    def test_standard_course(self):
        """
        Ensure that a course that cannot be verified does
        not have a visible verification sock.
        """
        response = self.client.get(course_home_url(self.standard_course))
        self.assert_verified_sock_is_not_visible(self.standard_course,
                                                 response)

    @override_waffle_flag(DISPLAY_COURSE_SOCK_FLAG, active=True)
    def test_verified_course(self):
        """
        Ensure that a course that can be verified has a
        visible verification sock.
        """
        response = self.client.get(course_home_url(self.verified_course))
        self.assert_verified_sock_is_visible(self.verified_course, response)

    @override_waffle_flag(DISPLAY_COURSE_SOCK_FLAG, active=True)
    def test_verified_course_updated_expired(self):
        """
        Ensure that a course that has an expired upgrade
        date does not display the verification sock.
        """
        response = self.client.get(
            course_home_url(self.verified_course_update_expired))
        self.assert_verified_sock_is_not_visible(
            self.verified_course_update_expired, response)

    @override_waffle_flag(DISPLAY_COURSE_SOCK_FLAG, active=True)
    def test_verified_course_user_already_upgraded(self):
        """
        Ensure that a user that has already upgraded to a
        verified status cannot see the verification sock.
        """
        response = self.client.get(
            course_home_url(self.verified_course_already_enrolled))
        self.assert_verified_sock_is_not_visible(
            self.verified_course_already_enrolled, response)

    @override_waffle_flag(DISPLAY_COURSE_SOCK_FLAG, active=True)
    @mock.patch(
        'openedx.features.course_experience.views.course_sock.format_strikeout_price',
        mock.Mock(return_value=(HTML("<span>DISCOUNT_PRICE</span>"), True)))
    def test_upgrade_message_discount(self):
        response = self.client.get(course_home_url(self.verified_course))
        self.assertContains(response, "<span>DISCOUNT_PRICE</span>")

    def assert_verified_sock_is_visible(self, course, response):
        return self.assertContains(response,
                                   TEST_VERIFICATION_SOCK_LOCATOR,
                                   html=False)

    def assert_verified_sock_is_not_visible(self, course, response):
        return self.assertNotContains(response,
                                      TEST_VERIFICATION_SOCK_LOCATOR,
                                      html=False)
class CourseHomeFragmentViewTests(ModuleStoreTestCase):
    """
    Test Messages Displayed on the Course Home
    """
    CREATE_USER = False

    def setUp(self):
        super(CourseHomeFragmentViewTests, self).setUp()
        CommerceConfiguration.objects.create(checkout_on_ecommerce_service=True)

        end = now() + timedelta(days=30)
        self.course = CourseFactory(
            start=now() - timedelta(days=30),
            end=end,
        )
        self.url = course_home_url(self.course)

        CourseMode.objects.create(course_id=self.course.id, mode_slug=CourseMode.AUDIT)
        self.verified_mode = CourseMode.objects.create(
            course_id=self.course.id,
            mode_slug=CourseMode.VERIFIED,
            min_price=100,
            expiration_datetime=end,
            sku='test'
        )

        self.user = UserFactory()
        self.client.login(username=self.user.username, password=TEST_PASSWORD)

        name = SHOW_UPGRADE_MSG_ON_COURSE_HOME.waffle_namespace._namespaced_name(
            SHOW_UPGRADE_MSG_ON_COURSE_HOME.flag_name)
        self.flag, __ = Flag.objects.update_or_create(name=name, defaults={'everyone': True})

    def assert_upgrade_message_not_displayed(self):
        response = self.client.get(self.url)
        self.assertNotContains(response, 'section-upgrade')

    def assert_upgrade_message_displayed(self):
        response = self.client.get(self.url)
        self.assertContains(response, 'section-upgrade')
        url = EcommerceService().get_checkout_page_url(self.verified_mode.sku)
        self.assertContains(response, '<a class="btn-brand btn-upgrade"')
        self.assertContains(response, url)
        self.assertContains(
            response,
            u"Upgrade (<span class='price'>${price}</span>)".format(price=self.verified_mode.min_price),
        )

    def test_no_upgrade_message_if_logged_out(self):
        self.client.logout()
        self.assert_upgrade_message_not_displayed()

    def test_no_upgrade_message_if_not_enrolled(self):
        self.assertEqual(len(CourseEnrollment.enrollments_for_user(self.user)), 0)
        self.assert_upgrade_message_not_displayed()

    def test_no_upgrade_message_if_verified_track(self):
        CourseEnrollment.enroll(self.user, self.course.id, CourseMode.VERIFIED)
        self.assert_upgrade_message_not_displayed()

    def test_no_upgrade_message_if_upgrade_deadline_passed(self):
        self.verified_mode.expiration_datetime = now() - timedelta(days=20)
        self.verified_mode.save()
        self.assert_upgrade_message_not_displayed()

    def test_no_upgrade_message_if_flag_disabled(self):
        self.flag.everyone = False
        self.flag.save()
        CourseEnrollment.enroll(self.user, self.course.id, CourseMode.AUDIT)
        self.assert_upgrade_message_not_displayed()

    def test_display_upgrade_message_if_audit_and_deadline_not_passed(self):
        CourseEnrollment.enroll(self.user, self.course.id, CourseMode.AUDIT)
        self.assert_upgrade_message_displayed()

    @mock.patch(
        'openedx.features.course_experience.views.course_home.format_strikeout_price',
        mock.Mock(return_value=(HTML("<span>DISCOUNT_PRICE</span>"), True))
    )
    def test_upgrade_message_discount(self):
        CourseEnrollment.enroll(self.user, self.course.id, CourseMode.AUDIT)

        with SHOW_UPGRADE_MSG_ON_COURSE_HOME.override(True):
            response = self.client.get(self.url)

        self.assertContains(response, "<span>DISCOUNT_PRICE</span>")
class TestProblemTypeAccess(SharedModuleStoreTestCase, MasqueradeMixin):

    PROBLEM_TYPES = ['problem', 'openassessment', 'drag-and-drop-v2', 'done', 'edx_sga']
    # 'html' is a component that just displays html, in these tests it is used to test that users who do not have access
    # to graded problems still have access to non-problems
    COMPONENT_TYPES = PROBLEM_TYPES + ['html']
    MODE_TYPES = ['credit', 'honor', 'audit', 'verified', 'professional', 'no-id-professional']

    GRADED_SCORE_WEIGHT_TEST_CASES = [
        # graded, has_score, weight, is_gated
        (False, False, 0, False),
        (False, True, 0, False),
        (False, False, 1, False),
        (False, True, 1, False),
        (True, False, 0, False),
        (True, True, 0, False),
        (True, False, 1, False),
        (True, True, 1, True)
    ]

    @classmethod
    def setUpClass(cls):
        super(TestProblemTypeAccess, cls).setUpClass()
        cls.factory = RequestFactory()

        cls.courses = {}

        # default course is used for most tests, it includes an audit and verified track and all the problem types
        # defined in 'PROBLEM_TYPES' and 'GRADED_SCORE_WEIGHT_TEST_CASES'
        cls.courses['default'] = cls._create_course(
            run='testcourse1',
            display_name='Test Course Title',
            modes=['audit', 'verified'],
            component_types=cls.COMPONENT_TYPES
        )
        # because default course is used for most tests self.course and self.problem_dict are set for ease of reference
        cls.course = cls.courses['default']['course']
        cls.blocks_dict = cls.courses['default']['blocks']

        # Create components with the cartesian product of possible values of
        # graded/has_score/weight for the test_graded_score_weight_values test.
        cls.graded_score_weight_blocks = {}
        for graded, has_score, weight, gated in cls.GRADED_SCORE_WEIGHT_TEST_CASES:
            case_name = ' Graded: ' + str(graded) + ' Has Score: ' + str(has_score) + ' Weight: ' + str(weight)
            block = ItemFactory.create(
                parent=cls.blocks_dict['vertical'],
                # has_score is determined by XBlock type. It is not a value set on an instance of an XBlock.
                # Therefore, we create a problem component when has_score is True
                # and an html component when has_score is False.
                category='problem' if has_score else 'html',
                graded=graded,
                weight=weight,
                metadata=METADATA if (graded and has_score and weight) else {},
            )
            # Intersperse HTML so that the content-gating renders in all blocks
            ItemFactory.create(
                parent=cls.blocks_dict['vertical'],
                category='html',
                graded=False,
            )
            cls.graded_score_weight_blocks[(graded, has_score, weight)] = block

        host = os.environ.get('BOK_CHOY_HOSTNAME', '127.0.0.1')
        metadata_lti_xblock = {
            'lti_id': 'correct_lti_id',
            'launch_url': 'http://{}:{}/{}'.format(host, '8765', 'correct_lti_endpoint'),
            'open_in_a_new_page': False
        }

        scored_lti_metadata = {}
        scored_lti_metadata.update(metadata_lti_xblock)
        scored_lti_metadata.update(METADATA)

        # add LTI blocks to default course
        cls.blocks_dict['lti_block'] = ItemFactory.create(
            parent=cls.blocks_dict['vertical'],
            category='lti_consumer',
            has_score=True,
            graded=True,
            metadata=scored_lti_metadata,
        )
        # Intersperse HTML so that the content-gating renders in all blocks
        ItemFactory.create(
            parent=cls.blocks_dict['vertical'],
            category='html',
            graded=False,
        )
        cls.blocks_dict['lti_block_not_scored'] = ItemFactory.create(
            parent=cls.blocks_dict['vertical'],
            category='lti_consumer',
            has_score=False,
            metadata=metadata_lti_xblock,
        )

        # Intersperse HTML so that the content-gating renders in all blocks
        ItemFactory.create(
            parent=cls.blocks_dict['vertical'],
            category='html',
            graded=False,
        )

        # add ungraded problem for xblock_handler test
        cls.blocks_dict['graded_problem'] = ItemFactory.create(
            parent=cls.blocks_dict['vertical'],
            category='problem',
            graded=True,
            metadata=METADATA,
        )

        # Intersperse HTML so that the content-gating renders in all blocks
        ItemFactory.create(
            parent=cls.blocks_dict['vertical'],
            category='html',
            graded=False,
        )

        cls.blocks_dict['ungraded_problem'] = ItemFactory.create(
            parent=cls.blocks_dict['vertical'],
            category='problem',
            graded=False,
        )

        # Intersperse HTML so that the content-gating renders in all blocks
        ItemFactory.create(
            parent=cls.blocks_dict['vertical'],
            category='html',
            graded=False,
        )

        cls.blocks_dict['audit_visible_graded_problem'] = ItemFactory.create(
            parent=cls.blocks_dict['vertical'],
            category='problem',
            graded=True,
            group_access={
                CONTENT_GATING_PARTITION_ID: [
                    CONTENT_TYPE_GATE_GROUP_IDS['limited_access'],
                    CONTENT_TYPE_GATE_GROUP_IDS['full_access']
                ]
            },
        )

        # Intersperse HTML so that the content-gating renders in all blocks
        ItemFactory.create(
            parent=cls.blocks_dict['vertical'],
            category='html',
            graded=False,
        )

        # audit_only course only has an audit track available
        cls.courses['audit_only'] = cls._create_course(
            run='audit_only_course_run_1',
            display_name='Audit Only Test Course Title',
            modes=['audit'],
            component_types=['problem', 'html']
        )

        # all_track_types course has all track types defined in MODE_TYPES
        cls.courses['all_track_types'] = cls._create_course(
            run='all_track_types_run_1',
            display_name='All Track/Mode Types Test Course Title',
            modes=cls.MODE_TYPES,
            component_types=['problem', 'html']
        )

        cls.courses['expired_upgrade_deadline'] = cls._create_course(
            run='expired_upgrade_deadline_run_1',
            display_name='Expired Upgrade Deadline Course Title',
            modes=['audit', 'verified'],
            component_types=['problem', 'html'],
            expired_upgrade_deadline=True
        )

    def setUp(self):
        super(TestProblemTypeAccess, self).setUp()

        # enroll all users into the all track types course
        self.users = {}
        for mode_type in self.MODE_TYPES:
            self.users[mode_type] = UserFactory.create(username=mode_type)
            CourseEnrollmentFactory.create(
                user=self.users[mode_type],
                course_id=self.courses['all_track_types']['course'].id,
                mode=mode_type
            )

        # create audit_user for ease of reference
        self.audit_user = self.users['audit']

        # enroll audit and verified users into default course
        for mode_type in ['audit', 'verified']:
            CourseEnrollmentFactory.create(
                user=self.users[mode_type],
                course_id=self.course.id,
                mode=mode_type
            )

        # enroll audit user into the audit_only course
        CourseEnrollmentFactory.create(
            user=self.audit_user,
            course_id=self.courses['audit_only']['course'].id,
            mode='audit'
        )
        # enroll audit user into the upgrade expired course
        CourseEnrollmentFactory.create(
            user=self.audit_user,
            course_id=self.courses['expired_upgrade_deadline']['course'].id,
            mode='audit'
        )
        ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1))

    @classmethod
    def _create_course(cls, run, display_name, modes, component_types, expired_upgrade_deadline=False):
        """
        Helper method to create a course
        Arguments:
            run (str): name of course run
            display_name (str): display name of course
            modes (list of str): list of modes/tracks this course should have
            component_types (list of str): list of problem types this course should have
        Returns:
             (dict): {
                'course': (CourseDescriptorWithMixins): course definition
                'blocks': (dict) {
                    'block_category_1': XBlock representing that block,
                    'block_category_2': XBlock representing that block,
                    ....
             }
        """
        start_date = timezone.now() - timedelta(weeks=1)
        course = CourseFactory.create(run=run, display_name=display_name, start=start_date)

        for mode in modes:
            if expired_upgrade_deadline and mode == 'verified':
                CourseModeFactory.create(
                    course_id=course.id,
                    mode_slug=mode,
                    expiration_datetime=start_date + timedelta(days=365),
                )
            else:
                CourseModeFactory.create(course_id=course.id, mode_slug=mode)

        with cls.store.bulk_operations(course.id):
            blocks_dict = {}
            chapter = ItemFactory.create(
                parent=course,
                display_name='Overview',
            )
            blocks_dict['chapter'] = ItemFactory.create(
                parent=course,
                category='chapter',
                display_name='Week 1',
            )
            blocks_dict['sequential'] = ItemFactory.create(
                parent=chapter,
                category='sequential',
                display_name='Lesson 1',
            )
            blocks_dict['vertical'] = ItemFactory.create(
                parent=blocks_dict['sequential'],
                category='vertical',
                display_name='Lesson 1 Vertical - Unit 1',
            )

            for component_type in component_types:
                block = ItemFactory.create(
                    parent=blocks_dict['vertical'],
                    category=component_type,
                    graded=True,
                    metadata={} if (component_type == 'html' or len(modes) == 1) else METADATA
                )
                blocks_dict[component_type] = block
                # Intersperse HTML so that the content-gating renders in all blocks
                ItemFactory.create(
                    parent=blocks_dict['vertical'],
                    category='html',
                    graded=False,
                )

            return {
                'course': course,
                'blocks': blocks_dict,
            }

    @ddt.data(
        ('problem', True),
        ('openassessment', True),
        ('drag-and-drop-v2', True),
        ('done', True),
        ('edx_sga', True),
        ('lti_block', True),
        ('ungraded_problem', False),
        ('lti_block_not_scored', False),
        ('audit_visible_graded_problem', False),
    )
    @ddt.unpack
    def test_access_to_problems(self, prob_type, is_gated):
        _assert_block_is_gated(
            block=self.blocks_dict[prob_type],
            user=self.users['audit'],
            course=self.course,
            is_gated=is_gated,
            request_factory=self.factory,
        )
        _assert_block_is_gated(
            block=self.blocks_dict[prob_type],
            user=self.users['verified'],
            course=self.course,
            is_gated=False,
            request_factory=self.factory,
        )

    @ddt.data(
        *GRADED_SCORE_WEIGHT_TEST_CASES
    )
    @ddt.unpack
    def test_graded_score_weight_values(self, graded, has_score, weight, is_gated):
        # Verify that graded, has_score and weight must all be true for a component to be gated
        block = self.graded_score_weight_blocks[(graded, has_score, weight)]
        _assert_block_is_gated(
            block=block,
            user=self.audit_user,
            course=self.course,
            is_gated=is_gated,
            request_factory=self.factory,
        )

    @ddt.data(
        ('audit', 'problem', 'default', True),
        ('verified', 'problem', 'default', False),
        ('audit', 'html', 'default', False),
        ('verified', 'html', 'default', False),
        ('audit', 'problem', 'audit_only', False),
        ('audit', 'html', 'audit_only', False),
        ('credit', 'problem', 'all_track_types', False),
        ('credit', 'html', 'all_track_types', False),
        ('honor', 'problem', 'all_track_types', False),
        ('honor', 'html', 'all_track_types', False),
        ('audit', 'problem', 'all_track_types', True),
        ('audit', 'html', 'all_track_types', False),
        ('verified', 'problem', 'all_track_types', False),
        ('verified', 'html', 'all_track_types', False),
        ('professional', 'problem', 'all_track_types', False),
        ('professional', 'html', 'all_track_types', False),
        ('no-id-professional', 'problem', 'all_track_types', False),
        ('no-id-professional', 'html', 'all_track_types', False),
    )
    @ddt.unpack
    def test_access_based_on_track(self, user_track, component_type, course, is_gated):
        """
         If a user is enrolled as an audit user they should not have access to graded problems, unless there is no paid
         track option.  All paid type tracks should have access to all types of content.
         All users should have access to non-problem component types, the 'html' components test that.
         """
        _assert_block_is_gated(
            block=self.courses[course]['blocks'][component_type],
            user=self.users[user_track],
            course=self.courses[course]['course'],
            is_gated=is_gated,
            request_factory=self.factory,
        )

    def test_access_expired_upgrade_deadline(self):
        """
        If a user is enrolled as an audit user and the upgrade deadline has passed
        the user will continue to see gated content, but the upgrade messaging will be removed.
        """
        _assert_block_is_gated(
            block=self.courses['expired_upgrade_deadline']['blocks']['problem'],
            user=self.users['audit'],
            course=self.courses['expired_upgrade_deadline']['course'],
            is_gated=True,
            request_factory=self.factory,
            has_upgrade_link=False
        )

    @ddt.data(
        ('problem', 'graded_problem', 'audit', 404),
        ('problem', 'graded_problem', 'verified', 200),
        ('problem', 'ungraded_problem', 'audit', 200),
        ('problem', 'ungraded_problem', 'verified', 200),
    )
    @ddt.unpack
    def test_xblock_handlers(self, xblock_type, xblock_name, user, status_code):
        """
        Test the ajax calls to the problem xblock to ensure the LMS is sending back
        the expected response codes on requests when content is gated for audit users
        (404) and when it is available to audit users (200). Content is always available
        to verified users.
        """
        problem_location = self.blocks_dict[xblock_name].scope_ids.usage_id
        url = reverse(
            'xblock_handler',
            kwargs={
                'course_id': six.text_type(self.course.id),
                'usage_id': quote_slashes(six.text_type(problem_location)),
                'handler': 'xmodule_handler',
                'suffix': 'problem_show',
            }
        )
        self.client.login(username=self.users[user].username, password=TEST_PASSWORD)
        response = self.client.post(url)
        self.assertEqual(response.status_code, status_code)

    @ddt.data(
        InstructorFactory,
        StaffFactory,
        BetaTesterFactory,
        OrgStaffFactory,
        OrgInstructorFactory,
        GlobalStaffFactory,
    )
    def test_access_course_team_users(self, role_factory):
        """
        Test that members of the course team do not lose access to graded content
        """
        # There are two types of course team members: instructor and staff
        # they have different privileges, but for the purpose of this test the important thing is that they should both
        # have access to all graded content
        if role_factory == GlobalStaffFactory:
            user = role_factory.create()
        else:
            user = role_factory.create(course_key=self.course.id)

        CourseEnrollmentFactory.create(
            user=user,
            course_id=self.course.id,
            mode='audit',
        )

        # assert that course team members have access to graded content
        _assert_block_is_gated(
            block=self.blocks_dict['problem'],
            user=user,
            course=self.course,
            is_gated=False,
            request_factory=self.factory,
        )

    @ddt.data(
        FORUM_ROLE_COMMUNITY_TA,
        FORUM_ROLE_ADMINISTRATOR,
        FORUM_ROLE_MODERATOR,
        FORUM_ROLE_GROUP_MODERATOR
    )
    def test_access_user_with_forum_role(self, role_name):
        """
        Test that users with a given forum role do not lose access to graded content
        """
        user = UserFactory.create()
        role = RoleFactory(name=role_name, course_id=self.course.id)
        role.users.add(user)

        CourseEnrollmentFactory.create(
            user=user,
            course_id=self.course.id,
            mode='audit',
        )
        _assert_block_is_gated(
            block=self.blocks_dict['problem'],
            user=user,
            course=self.course,
            is_gated=False,
            request_factory=self.factory,
        )

    @ddt.data(
        (False, True),
        (True, False),
    )
    @ddt.unpack
    def test_content_gating_holdback(self, put_user_in_holdback, is_gated):
        """
        Test that putting a user in the content gating holdback disables content gating.
        """
        user = UserFactory.create()
        enrollment = CourseEnrollment.enroll(user, self.course.id)
        if put_user_in_holdback:
            FBEEnrollmentExclusion.objects.create(enrollment=enrollment)

        graded, has_score, weight = True, True, 1
        block = self.graded_score_weight_blocks[(graded, has_score, weight)]
        _assert_block_is_gated(
            block=block,
            user=user,
            course=self.course,
            is_gated=is_gated,
            request_factory=self.factory,
        )

    @ddt.data(
        ({'user_partition_id': CONTENT_GATING_PARTITION_ID,
          'group_id': CONTENT_TYPE_GATE_GROUP_IDS['limited_access']}, True),
        ({'user_partition_id': CONTENT_GATING_PARTITION_ID,
          'group_id': CONTENT_TYPE_GATE_GROUP_IDS['full_access']}, False),
        ({'user_partition_id': ENROLLMENT_TRACK_PARTITION_ID,
          'group_id': settings.COURSE_ENROLLMENT_MODES['audit']['id']}, True),
        ({'user_partition_id': ENROLLMENT_TRACK_PARTITION_ID,
          'group_id': settings.COURSE_ENROLLMENT_MODES['verified']['id']}, False),
        ({'role': 'staff'}, False),
        ({'role': 'student'}, True),
        ({'username': '******'}, True),
        ({'username': '******'}, False),
    )
    @ddt.unpack
    def test_masquerade(self, masquerade_config, is_gated):
        instructor = UserFactory.create()
        CourseEnrollmentFactory.create(
            user=instructor,
            course_id=self.course.id,
            mode='audit'
        )
        CourseInstructorRole(self.course.id).add_users(instructor)
        self.client.login(username=instructor.username, password=TEST_PASSWORD)

        self.update_masquerade(**masquerade_config)

        block = self.blocks_dict['problem']
        block_view_url = reverse('render_xblock', kwargs={'usage_key_string': six.text_type(block.scope_ids.usage_id)})
        response = self.client.get(block_view_url)
        if is_gated:
            self.assertEqual(response.status_code, 404)
        else:
            self.assertEqual(response.status_code, 200)

    @ddt.data(
        InstructorFactory,
        StaffFactory,
        BetaTesterFactory,
        OrgStaffFactory,
        OrgInstructorFactory,
        GlobalStaffFactory,
    )
    def test_access_masquerade_as_course_team_users(self, role_factory):
        """
        Test that when masquerading as members of the course team you do not lose access to graded content
        """
        # There are two types of course team members: instructor and staff
        # they have different privileges, but for the purpose of this test the important thing is that they should both
        # have access to all graded content
        staff_user = StaffFactory.create(password=TEST_PASSWORD, course_key=self.course.id)
        CourseEnrollmentFactory.create(
            user=staff_user,
            course_id=self.course.id,
            mode='audit'
        )
        self.client.login(username=staff_user.username, password=TEST_PASSWORD)

        if role_factory == GlobalStaffFactory:
            user = role_factory.create()
        else:
            user = role_factory.create(course_key=self.course.id)
        CourseEnrollment.enroll(user, self.course.id)
        self.update_masquerade(username=user.username)

        block = self.blocks_dict['problem']
        block_view_url = reverse('render_xblock', kwargs={'usage_key_string': six.text_type(block.scope_ids.usage_id)})
        response = self.client.get(block_view_url)
        self.assertEqual(response.status_code, 200)

    @ddt.data(
        FORUM_ROLE_COMMUNITY_TA,
        FORUM_ROLE_ADMINISTRATOR,
        FORUM_ROLE_MODERATOR,
        FORUM_ROLE_GROUP_MODERATOR
    )
    def test_access_masquerade_as_user_with_forum_role(self, role_name):
        """
        Test that when masquerading as a user with a given forum role you do not lose access to graded content
        """
        staff_user = StaffFactory.create(password=TEST_PASSWORD, course_key=self.course.id)
        CourseEnrollmentFactory.create(
            user=staff_user,
            course_id=self.course.id,
            mode='audit'
        )
        self.client.login(username=staff_user.username, password=TEST_PASSWORD)

        user = UserFactory.create()
        role = RoleFactory(name=role_name, course_id=self.course.id)
        role.users.add(user)

        CourseEnrollmentFactory.create(
            user=user,
            course_id=self.course.id,
            mode='audit'
        )

        self.update_masquerade(username=user.username)

        _assert_block_is_gated(
            block=self.blocks_dict['problem'],
            user=user,
            course=self.course,
            is_gated=False,
            request_factory=self.factory,
        )

    @patch(
        'openedx.features.content_type_gating.partitions.format_strikeout_price',
        Mock(return_value=(HTML("<span>DISCOUNT_PRICE</span>"), True))
    )
    def test_discount_display(self):

        with patch.object(ContentTypeGatingPartition, '_get_checkout_link', return_value='#'):
            block_content = _get_content_from_lms_index(
                block=self.blocks_dict['problem'],
                user_id=self.audit_user.id,
                course=self.course,
                request_factory=self.factory,
            )

        assert '<span>DISCOUNT_PRICE</span>' in block_content
Example #28
0
    def response_a11y_data(self, response, inputfields, responsetype_id, problem_data):
        """
        Construct data to be used for a11y.

        Arguments:
            response (object): xml response object
            inputfields (list): list of inputfields in a responsetype
            responsetype_id (str): responsetype id
            problem_data (dict): dict to be filled with response data
        """
        # if there are no inputtypes then don't do anything
        if not inputfields:
            return

        element_to_be_deleted = None
        label = ''

        if len(inputfields) > 1:
            response.set('multiple_inputtypes', 'true')
            group_label_tag = response.find('label')
            group_description_tags = response.findall('description')
            group_label_tag_id = u'multiinput-group-label-{}'.format(responsetype_id)
            group_label_tag_text = ''
            if group_label_tag is not None:
                group_label_tag.tag = 'p'
                group_label_tag.set('id', group_label_tag_id)
                group_label_tag.set('class', 'multi-inputs-group-label')
                group_label_tag_text = stringify_children(group_label_tag)
                response.set('multiinput-group-label-id', group_label_tag_id)

            group_description_ids = []
            for index, group_description_tag in enumerate(group_description_tags):
                group_description_tag_id = u'multiinput-group-description-{}-{}'.format(responsetype_id, index)
                group_description_tag.tag = 'p'
                group_description_tag.set('id', group_description_tag_id)
                group_description_tag.set('class', 'multi-inputs-group-description question-description')
                group_description_ids.append(group_description_tag_id)

            if group_description_ids:
                response.set('multiinput-group_description_ids', ' '.join(group_description_ids))

            for inputfield in inputfields:
                problem_data[inputfield.get('id')] = {
                    'group_label': group_label_tag_text,
                    'label': inputfield.attrib.get('label', ''),
                    'descriptions': {}
                }
        else:
            # Extract label value from <label> tag or label attribute from inside the responsetype
            responsetype_label_tag = response.find('label')
            if responsetype_label_tag is not None:
                label = stringify_children(responsetype_label_tag)
                # store <label> tag containing question text to delete
                # it later otherwise question will be rendered twice
                element_to_be_deleted = responsetype_label_tag
            elif 'label' in inputfields[0].attrib:
                # in this case we have old problems with label attribute and p tag having question in it
                # we will pick the first sibling of responsetype if its a p tag and match the text with
                # the label attribute text. if they are equal then we will use this text as question.
                # Get first <p> tag before responsetype, this <p> may contains the question text.
                p_tag = response.xpath('preceding-sibling::*[1][self::p]')

                if p_tag and p_tag[0].text == inputfields[0].attrib['label']:
                    label = stringify_children(p_tag[0])
                    element_to_be_deleted = p_tag[0]
            else:
                # In this case the problems don't have tag or label attribute inside the responsetype
                # so we will get the first preceding label tag w.r.t to this responsetype.
                # This will take care of those multi-question problems that are not using --- in their markdown.
                label_tag = response.xpath('preceding-sibling::*[1][self::label]')
                if label_tag:
                    label = stringify_children(label_tag[0])
                    element_to_be_deleted = label_tag[0]

            # delete label or p element only if inputtype is fully accessible
            if inputfields[0].tag in ACCESSIBLE_CAPA_INPUT_TYPES and element_to_be_deleted is not None:
                element_to_be_deleted.getparent().remove(element_to_be_deleted)

            # Extract descriptions and set unique id on each description tag
            description_tags = response.findall('description')
            description_id = 1
            descriptions = OrderedDict()
            for description in description_tags:
                descriptions[
                    "description_%s_%i" % (responsetype_id, description_id)
                ] = HTML(stringify_children(description))
                response.remove(description)
                description_id += 1

            problem_data[inputfields[0].get('id')] = {
                'label': HTML(label.strip()) if label else '',
                'descriptions': descriptions
            }
Example #29
0
    def _add_honor_code_field(self, form_desc, required=True):
        """Add an honor code field to a form description.
        Arguments:
            form_desc: A form description
        Keyword Arguments:
            required (bool): Whether this field is required; defaults to True
        """

        separate_honor_and_tos = self._is_field_visible("terms_of_service")
        # Separate terms of service and honor code checkboxes
        if separate_honor_and_tos:
            terms_label = _(u"Honor Code")
            terms_link = marketing_link("HONOR")

        # Combine terms of service and honor code checkboxes
        else:
            # Translators: This is a legal document users must agree to
            # in order to register a new account.
            terms_label = _(u"Terms of Service and Honor Code")
            #terms_link = marketing_link("HONOR")
            terms_link = "/tos"

        # Translators: "Terms of Service" is a legal document users must agree to
        # in order to register a new account.
        label = Text(
            _(u"I agree to the {platform_name} {terms_of_service_link_start}{terms_of_service}{terms_of_service_link_end}"
              )).format(
                  platform_name=configuration_helpers.get_value(
                      "PLATFORM_NAME", settings.PLATFORM_NAME),
                  terms_of_service=terms_label,
                  terms_of_service_link_start=HTML(
                      "<a href='{terms_link}' target='_blank'>").format(
                          terms_link=terms_link),
                  terms_of_service_link_end=HTML("</a>"),
              )

        # Translators: "Terms of Service" is a legal document users must agree to
        # in order to register a new account.
        error_msg = _(
            u"You must agree to the {platform_name} {terms_of_service}"
        ).format(platform_name=configuration_helpers.get_value(
            "PLATFORM_NAME", settings.PLATFORM_NAME),
                 terms_of_service=terms_label)
        field_type = 'checkbox'

        if not separate_honor_and_tos:
            current_request = crum.get_current_request()

            field_type = 'plaintext'

            pp_link = marketing_link("PRIVACY")
            label = Text(
                _(u"By creating an account with {platform_name}, you agree \
                  to abide by our {platform_name} \
                  {terms_of_service_link_start}{terms_of_service}{terms_of_service_link_end} \
                  and agree to our {privacy_policy_link_start}Privacy Policy{privacy_policy_link_end}."
                  )).format(
                      platform_name=configuration_helpers.get_value(
                          "PLATFORM_NAME", settings.PLATFORM_NAME),
                      terms_of_service=terms_label,
                      terms_of_service_link_start=HTML(
                          "<a href='{terms_url}' target='_blank'>").format(
                              terms_url=terms_link),
                      terms_of_service_link_end=HTML("</a>"),
                      privacy_policy_link_start=HTML(
                          "<a href='{pp_url}' target='_blank'>").format(
                              pp_url=pp_link),
                      privacy_policy_link_end=HTML("</a>"),
                  )

        form_desc.add_field(
            "honor_code",
            label=label,
            field_type=field_type,
            default=False,
            required=required,
            error_messages={"required": error_msg},
        )
Example #30
0
def render_body(context,
                online_help_token,
                use_cookie_banner=False,
                **pageargs):
    __M_caller = context.caller_stack._push_frame()
    try:
        __M_locals = __M_dict_builtin(use_cookie_banner=use_cookie_banner,
                                      pageargs=pageargs,
                                      online_help_token=online_help_token)
        _import_ns = {}
        _mako_get_namespace(context, '__anon_0x7f4938752a50')._populate(
            _import_ns, [u'login_query'])
        csrf_token = _import_ns.get('csrf_token',
                                    context.get('csrf_token', UNDEFINED))
        settings = _import_ns.get('settings',
                                  context.get('settings', UNDEFINED))

        def navigation_top():
            return render_navigation_top(context._locals(__M_locals))

        len = _import_ns.get('len', context.get('len', UNDEFINED))
        course = _import_ns.get('course', context.get('course', UNDEFINED))

        def js_extra():
            return render_js_extra(context._locals(__M_locals))

        static = _mako_get_namespace(context, 'static')
        user = _import_ns.get('user', context.get('user', UNDEFINED))
        LANGUAGE_CODE = _import_ns.get('LANGUAGE_CODE',
                                       context.get('LANGUAGE_CODE', UNDEFINED))
        uses_pattern_library = _import_ns.get(
            'uses_pattern_library',
            context.get('uses_pattern_library', UNDEFINED))
        __M_writer = context.writer()
        __M_writer(u'\n')
        __M_writer(u'\n\n')
        __M_writer(u'\n')
        __M_writer(u'\n')
        __M_writer(u'\n\n')
        if 'parent' not in context._data or not hasattr(
                context._data['parent'], 'navigation_top'):
            context['self'].navigation_top(**pageargs)

        __M_writer(u'\n\n')
        if uses_pattern_library:
            __M_writer(u'    ')
            if 'parent' not in context._data or not hasattr(
                    context._data['parent'], 'js_extra'):
                context['self'].js_extra(**pageargs)

            __M_writer(u'\n')
        __M_writer(u'\n')

        unsupported_browser_alert_versions = configuration_helpers.get_value(
            'UNSUPPORTED_BROWSER_ALERT_VERSIONS',
            settings.FEATURES.get('UNSUPPORTED_BROWSER_ALERT_VERSIONS'))

        __M_locals_builtin_stored = __M_locals_builtin()
        __M_locals.update(
            __M_dict_builtin([
                (__M_key, __M_locals_builtin_stored[__M_key])
                for __M_key in ['unsupported_browser_alert_versions']
                if __M_key in __M_locals_builtin_stored
            ]))
        __M_writer(u'\n')
        if waffle.switch_is_active('enable_unsupported_browser_alert'):
            __M_writer(u'  <script>\n    var $buoop = {\n        notify:')
            __M_writer(filters.decode.utf8(unsupported_browser_alert_versions))
            __M_writer(
                u',\n        api:5,\n        reminder:0\n    };\n    function $buo_f() {\n        var e = document.createElement("script");\n        e.src = "//browser-update.org/update.min.js";\n        document.body.appendChild(e);\n    };\n    try {document.addEventListener("DOMContentLoaded", $buo_f,false)}\n    catch(e){window.attachEvent("onload", $buo_f)}\n  </script>\n'
            )
        __M_writer(u'\n<header class="global-header ')
        __M_writer(
            filters.html_escape(filters.decode.utf8('slim' if course else '')))
        __M_writer(u'">\n')
        if use_cookie_banner:
            __M_writer(u'        ')
            __M_writer(
                filters.html_escape(
                    filters.decode.utf8(
                        static.renderReact(component="CookiePolicyBanner",
                                           id="cookie-policy-banner",
                                           props={}))))
            __M_writer(u'\n')
        __M_writer(u'    <div class="main-header">\n        ')
        runtime._include_file(context,
                              u'navbar-logo-header.html',
                              _template_uri,
                              online_help_token=online_help_token)
        __M_writer(
            u'\n        <div class="hamburger-menu" role="button" aria-label=')
        __M_writer(filters.html_escape(filters.decode.utf8(_("Options Menu"))))
        __M_writer(
            u' aria-expanded="false" aria-controls="mobile-menu" tabindex="0">\n            <span class="line"></span>\n            <span class="line"></span>\n            <span class="line"></span>\n            <span class="line"></span>\n        </div>\n'
        )
        if user.is_authenticated:
            __M_writer(u'            ')
            runtime._include_file(context,
                                  u'navbar-authenticated.html',
                                  _template_uri,
                                  online_help_token=online_help_token)
            __M_writer(u'\n')
        else:
            __M_writer(u'            ')
            runtime._include_file(context,
                                  u'navbar-not-authenticated.html',
                                  _template_uri,
                                  online_help_token=online_help_token)
            __M_writer(u'\n')
        __M_writer(
            u'    </div>\n    <div class="mobile-menu hidden" aria-label=')
        __M_writer(filters.html_escape(filters.decode.utf8(_("More Options"))))
        __M_writer(u' role="menu" id="mobile-menu"></div>\n</header>\n\n')
        if course:
            __M_writer(
                u'<!--[if lte IE 9]>\n<div class="ie-banner" aria-hidden="true">'
            )
            __M_writer(
                filters.html_escape(
                    filters.decode.utf8(
                        Text(
                            _('{begin_strong}Warning:{end_strong} Your browser is not fully supported. We strongly recommend using {chrome_link} or {ff_link}.'
                              )).
                        format(
                            begin_strong=HTML('<strong>'),
                            end_strong=HTML('</strong>'),
                            chrome_link=HTML(
                                '<a href="https://www.google.com/chrome" target="_blank">Chrome</a>'
                            ),
                            ff_link=HTML(
                                '<a href="http://www.mozilla.org/firefox" target="_blank">Firefox</a>'
                            ),
                        ))))
            __M_writer(u'</div>\n<![endif]-->\n')
        __M_writer(u'\n')
        if settings.FEATURES.get('ENABLE_COOKIE_CONSENT', False):
            __M_writer(u'  ')
            runtime._include_file(context, u'../widgets/cookie-consent.html',
                                  _template_uri)
            __M_writer(u'\n')
        __M_writer(u'\n')
        if header_language_selector_is_enabled():
            __M_writer(u'    ')
            languages = released_languages()

            __M_locals_builtin_stored = __M_locals_builtin()
            __M_locals.update(
                __M_dict_builtin([(__M_key, __M_locals_builtin_stored[__M_key])
                                  for __M_key in ['languages']
                                  if __M_key in __M_locals_builtin_stored]))
            __M_writer(u'\n')
            if len(languages) > 1:
                __M_writer(
                    u'        <form action="/i18n/setlang/" method="post" class="settings-language-form" id="language-settings-form">\n            <input type="hidden" id="csrf_token" name="csrfmiddlewaretoken" value="'
                )
                __M_writer(filters.html_escape(
                    filters.decode.utf8(csrf_token)))
                __M_writer(u'">\n')
                if user.is_authenticated:
                    __M_writer(
                        u'                <input title="preference api" type="hidden" class="url-endpoint" value="'
                    )
                    __M_writer(
                        filters.html_escape(
                            filters.decode.utf8(
                                reverse('preferences_api',
                                        kwargs={'username': user.username}))))
                    __M_writer(u'" data-user-is-authenticated="true">\n')
                else:
                    __M_writer(
                        u'                <input title="session update url" type="hidden" class="url-endpoint" value="'
                    )
                    __M_writer(
                        filters.html_escape(
                            filters.decode.utf8(reverse('session_language'))))
                    __M_writer(u'" data-user-is-authenticated="false">\n')
                __M_writer(u'            <label><span class="sr">')
                __M_writer(
                    filters.html_escape(
                        filters.decode.utf8(_("Choose Language"))))
                __M_writer(
                    u'</span>\n            <select class="input select language-selector" id="settings-language-value" name="language">\n'
                )
                for language in languages:
                    if language[0] == LANGUAGE_CODE:
                        __M_writer(u'                        <option value="')
                        __M_writer(
                            filters.html_escape(
                                filters.decode.utf8(language[0])))
                        __M_writer(u'" selected="selected">')
                        __M_writer(
                            filters.html_escape(
                                filters.decode.utf8(language[1])))
                        __M_writer(u'</option>\n')
                    else:
                        __M_writer(u'                        <option value="')
                        __M_writer(
                            filters.html_escape(
                                filters.decode.utf8(language[0])))
                        __M_writer(u'" >')
                        __M_writer(
                            filters.html_escape(
                                filters.decode.utf8(language[1])))
                        __M_writer(u'</option>\n')
                __M_writer(
                    u'            </select>\n            </label>\n        </form>\n'
                )
        return ''
    finally:
        context.caller_stack._pop_frame()