예제 #1
0
def send_refund_notification(course_enrollment, refund_ids):
    """
    Issue an email notification to the configured email recipient about a
    newly-initiated refund request.

    This function does not do any exception handling; callers are responsible
    for capturing and recovering from any errors.
    """
    if microsite.is_request_in_microsite():
        # this is not presently supported with the external service.
        raise NotImplementedError("Unable to send refund processing emails to microsite teams.")

    for_user = course_enrollment.user
    subject = _("[Refund] User-Requested Refund")
    message = _(
        "A refund request has been initiated for {username} ({email}). To process this request, please visit the link(s) below."  # pylint: disable=line-too-long
    ).format(username=for_user.username, email=for_user.email)

    # TODO: this manipulation of API url is temporary, pending the introduction
    # of separate configuration items for the service's base url and api path.
    ecommerce_url = '://'.join(urlparse(settings.ECOMMERCE_API_URL)[:2])

    refund_urls = ["{}/dashboard/refunds/{}/".format(ecommerce_url, refund_id) for refund_id in refund_ids]
    text_body = '\r\n'.join([message] + refund_urls + [''])
    refund_links = ['<a href="{0}">{0}</a>'.format(url) for url in refund_urls]
    html_body = '<p>{}</p>'.format('<br>'.join([message] + refund_links))

    email_message = EmailMultiAlternatives(subject, text_body, for_user.email, [settings.PAYMENT_SUPPORT_EMAIL])
    email_message.attach_alternative(html_body, "text/html")
    email_message.send()
예제 #2
0
def get_template_path(relative_path, **kwargs):
    """
    This is a proxy function to hide microsite_configuration behind comprehensive theming.
    """
    if microsite.is_request_in_microsite():
        relative_path = microsite.get_template_path(relative_path, **kwargs)
    return relative_path
예제 #3
0
    def current(cls, *args):
        """
        Get the current config model for the provider according to the enabled slugs for this site.
        The site configuration expects the value of THIRD_PARTY_AUTH_ENABLED_PROVIDERS to be a dict
        of backend_name and the slug being used for the configuration object.
        E.g.
        "THIRD_PARTY_AUTH_ENABLED_PROVIDERS":{
            "google-oauth2":"my-slug-for-this-provider"
        }
        """
        enabled_providers = microsite.get_value('THIRD_PARTY_AUTH_ENABLED_PROVIDERS', {})

        # In a very specific case, azuread-oauth2 does not have a microsite context.
        if not microsite.is_request_in_microsite():
            try:
                microsite.set_by_domain(get_current_request().site.domain)
                enabled_providers = microsite.get_value('THIRD_PARTY_AUTH_ENABLED_PROVIDERS', {})
                microsite.clear()
            except Exception:  # pylint: disable=broad-except
                pass

        if not enabled_providers:
            return super(OAuth2ProviderConfig, cls).current(*args)
        provider_slug = enabled_providers.get(args[0])
        if provider_slug:
            return super(OAuth2ProviderConfig, cls).current(provider_slug)
        return super(OAuth2ProviderConfig, cls).current(None)
예제 #4
0
def send_refund_notification(course_enrollment, refund_ids):
    """
    Issue an email notification to the configured email recipient about a
    newly-initiated refund request.

    This function does not do any exception handling; callers are responsible
    for capturing and recovering from any errors.
    """
    if microsite.is_request_in_microsite():
        # this is not presently supported with the external service.
        raise NotImplementedError("Unable to send refund processing emails to microsite teams.")

    for_user = course_enrollment.user
    subject = _("[Refund] User-Requested Refund")
    message = _(
        "A refund request has been initiated for {username} ({email}). "
        "To process this request, please visit the link(s) below."
    ).format(username=for_user.username, email=for_user.email)

    refund_urls = [
        urljoin(settings.ECOMMERCE_PUBLIC_URL_ROOT, '/dashboard/refunds/{}/'.format(refund_id))
        for refund_id in refund_ids
    ]
    text_body = '\r\n'.join([message] + refund_urls + [''])
    refund_links = ['<a href="{0}">{0}</a>'.format(url) for url in refund_urls]
    html_body = '<p>{}</p>'.format('<br>'.join([message] + refund_links))

    email_message = EmailMultiAlternatives(subject, text_body, for_user.email, [settings.PAYMENT_SUPPORT_EMAIL])
    email_message.attach_alternative(html_body, "text/html")
    email_message.send()
예제 #5
0
def atp_check_certificate(request, course_id):
    log.info("atp_check_certificate start: " +
             str(datetime.datetime.now().strftime("%s")))
    context = {}
    try:
        user = request.user
        username = user.username
        course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
        course = get_course_by_id(course_key)
        is_passed = CourseGradeFactory().create(user, course).passed
        if is_passed:
            certificate_url = '/api/atp/generate/certificate/' + course_id + '/'
        else:
            certificate_url = ''
        context['course_id'] = course_id
        context['username'] = username
        context['passed'] = is_passed
        context['certificate_url'] = certificate_url
        context['microsite'] = is_request_in_microsite()
        context['status'] = True

    except:
        context['status'] = False
        context['message'] = 'Error'

    log.info("atp_check_certificate end: " +
             str(datetime.datetime.now().strftime("%s")))
    return JsonResponse(context)
예제 #6
0
def send_refund_notification(course_enrollment, refund_ids):
    """
    Issue an email notification to the configured email recipient about a
    newly-initiated refund request.

    This function does not do any exception handling; callers are responsible
    for capturing and recovering from any errors.
    """
    if microsite.is_request_in_microsite():
        # this is not presently supported with the external service.
        raise NotImplementedError("Unable to send refund processing emails to microsite teams.")

    for_user = course_enrollment.user
    subject = _("[Refund] User-Requested Refund")
    message = _(
        "A refund request has been initiated for {username} ({email}). To process this request, please visit the link(s) below."  # pylint: disable=line-too-long
    ).format(username=for_user.username, email=for_user.email)

    # TODO: this manipulation of API url is temporary, pending the introduction
    # of separate configuration items for the service's base url and api path.
    ecommerce_url = '://'.join(urlparse(settings.ECOMMERCE_API_URL)[:2])

    refund_urls = ["{}/dashboard/refunds/{}/".format(ecommerce_url, refund_id) for refund_id in refund_ids]
    text_body = '\r\n'.join([message] + refund_urls + [''])
    refund_links = ['<a href="{0}">{0}</a>'.format(url) for url in refund_urls]
    html_body = '<p>{}</p>'.format('<br>'.join([message] + refund_links))

    email_message = EmailMultiAlternatives(subject, text_body, for_user.email, [settings.PAYMENT_SUPPORT_EMAIL])
    email_message.attach_alternative(html_body, "text/html")
    email_message.send()
예제 #7
0
def is_request_in_themed_site():
    """
    This is a proxy function to hide microsite_configuration behind comprehensive theming.
    """
    # We need to give priority to theming/site-configuration over microsites
    return configuration_helpers.is_site_configuration_enabled(
    ) or microsite.is_request_in_microsite()
예제 #8
0
def get_template_path(relative_path, **kwargs):
    """
    This is a proxy function to hide microsite_configuration behind comprehensive theming.
    """
    if microsite.is_request_in_microsite():
        relative_path = microsite.get_template_path(relative_path, **kwargs)
    return relative_path
예제 #9
0
def send_refund_notification(course_enrollment, refund_ids):
    """
    Issue an email notification to the configured email recipient about a
    newly-initiated refund request.

    This function does not do any exception handling; callers are responsible
    for capturing and recovering from any errors.
    """
    if microsite.is_request_in_microsite():
        # this is not presently supported with the external service.
        raise NotImplementedError(
            "Unable to send refund processing emails to microsite teams.")

    for_user = course_enrollment.user
    subject = _("[Refund] User-Requested Refund")
    message = _(
        "A refund request has been initiated for {username} ({email}). "
        "To process this request, please visit the link(s) below.").format(
            username=for_user.username, email=for_user.email)

    refund_urls = [
        urljoin(settings.ECOMMERCE_PUBLIC_URL_ROOT,
                '/dashboard/refunds/{}/'.format(refund_id))
        for refund_id in refund_ids
    ]
    text_body = '\r\n'.join([message] + refund_urls + [''])
    refund_links = ['<a href="{0}">{0}</a>'.format(url) for url in refund_urls]
    html_body = '<p>{}</p>'.format('<br>'.join([message] + refund_links))

    email_message = EmailMultiAlternatives(subject, text_body, for_user.email,
                                           [settings.PAYMENT_SUPPORT_EMAIL])
    email_message.attach_alternative(html_body, "text/html")
    email_message.send()
예제 #10
0
def microsite_footer_context_processor(request):
    """
    Checks the site name to determine whether to use the edX.org footer or the Open Source Footer.
    """
    return dict(
        [
            ("IS_REQUEST_IN_MICROSITE", microsite.is_request_in_microsite())
        ]
    )
예제 #11
0
def microsite_footer_context_processor(request):
    """
    Checks the site name to determine whether to use the edX.org footer or the Open Source Footer.
    """
    return dict(
        [
            ("IS_REQUEST_IN_MICROSITE", microsite.is_request_in_microsite())
        ]
    )
예제 #12
0
def get_template_path(relative_path, **kwargs):
    """
    This is a proxy function to hide microsite_configuration behind comprehensive theming.
    """
    # We need to give priority to theming over microsites
    # So, we apply microsite override only if there is no associated site theme
    # and associated microsite is present.
    if not current_request_has_associated_site_theme() and microsite.is_request_in_microsite():
        relative_path = microsite.get_template_path(relative_path, **kwargs)
    return relative_path
예제 #13
0
def get_template_path(relative_path, **kwargs):
    """
    This is a proxy function to hide microsite_configuration behind comprehensive theming.
    """
    # We need to give priority to theming over microsites
    # So, we apply microsite override only if there is no associated site theme
    # and associated microsite is present.
    if not current_request_has_associated_site_theme() and microsite.is_request_in_microsite():
        relative_path = microsite.get_template_path(relative_path, **kwargs)
    return relative_path
예제 #14
0
def login_and_registration_form(request, initial_mode="login"):
    """Render the combined login/registration form, defaulting to login

    This relies on the JS to asynchronously load the actual form from
    the user_api.

    Keyword Args:
        initial_mode (string): Either "login" or "register".

    """
    # If we're already logged in, redirect to the dashboard
    if request.user.is_authenticated():
        return redirect(reverse('dashboard'))

    # Retrieve the form descriptions from the user API
    form_descriptions = _get_form_descriptions(request)

    # If this is a microsite, revert to the old login/registration pages.
    # We need to do this for now to support existing themes.
    if microsite.is_request_in_microsite():
        if initial_mode == "login":
            return old_login_view(request)
        elif initial_mode == "register":
            return old_register_view(request)

    # Allow external auth to intercept and handle the request
    ext_auth_response = _external_auth_intercept(request, initial_mode)
    if ext_auth_response is not None:
        return ext_auth_response

    # Otherwise, render the combined login/registration page
    context = {
        'disable_courseware_js': True,
        'initial_mode': initial_mode,
        'third_party_auth': json.dumps(_third_party_auth_context(request)),
        'platform_name': settings.PLATFORM_NAME,
        'responsive': True,

        # Include form descriptions retrieved from the user API.
        # We could have the JS client make these requests directly,
        # but we include them in the initial page load to avoid
        # the additional round-trip to the server.
        'login_form_desc': form_descriptions['login'],
        'registration_form_desc': form_descriptions['registration'],
        'password_reset_form_desc': form_descriptions['password_reset'],

        # We need to pass these parameters so that the header's
        # "Sign In" button preserves the querystring params.
        'enrollment_action': request.GET.get('enrollment_action'),
        'course_id': request.GET.get('course_id'),
        'course_mode': request.GET.get('course_mode'),
    }

    return render_to_response('student_account/login_and_register.html',
                              context)
예제 #15
0
def login_and_registration_form(request, initial_mode="login"):
    """Render the combined login/registration form, defaulting to login

    This relies on the JS to asynchronously load the actual form from
    the user_api.

    Keyword Args:
        initial_mode (string): Either "login" or "register".

    """
    # If we're already logged in, redirect to the dashboard
    if request.user.is_authenticated():
        return redirect(reverse('dashboard'))

    # Retrieve the form descriptions from the user API
    form_descriptions = _get_form_descriptions(request)

    # If this is a microsite, revert to the old login/registration pages.
    # We need to do this for now to support existing themes.
    if microsite.is_request_in_microsite():
        if initial_mode == "login":
            return old_login_view(request)
        elif initial_mode == "register":
            return old_register_view(request)

    # Allow external auth to intercept and handle the request
    ext_auth_response = _external_auth_intercept(request, initial_mode)
    if ext_auth_response is not None:
        return ext_auth_response

    # Otherwise, render the combined login/registration page
    context = {
        'disable_courseware_js': True,
        'initial_mode': initial_mode,
        'third_party_auth': json.dumps(_third_party_auth_context(request)),
        'platform_name': settings.PLATFORM_NAME,
        'responsive': True,

        # Include form descriptions retrieved from the user API.
        # We could have the JS client make these requests directly,
        # but we include them in the initial page load to avoid
        # the additional round-trip to the server.
        'login_form_desc': form_descriptions['login'],
        'registration_form_desc': form_descriptions['registration'],
        'password_reset_form_desc': form_descriptions['password_reset'],

        # We need to pass these parameters so that the header's
        # "Sign In" button preserves the querystring params.
        'enrollment_action': request.GET.get('enrollment_action'),
        'course_id': request.GET.get('course_id'),
        'course_mode': request.GET.get('course_mode'),
    }

    return render_to_response('student_account/login_and_register.html', context)
예제 #16
0
def _update_social_context(request, context, course, user, user_certificate, platform_name):
    """
    Updates context dictionary with info required for social sharing.
    """
    share_settings = getattr(settings, 'SOCIAL_SHARING_SETTINGS', {})
    context['facebook_share_enabled'] = share_settings.get('CERTIFICATE_FACEBOOK', False)
    context['facebook_app_id'] = getattr(settings, "FACEBOOK_APP_ID", None)
    context['facebook_share_text'] = share_settings.get(
        'CERTIFICATE_FACEBOOK_TEXT',
        _("I completed the {course_title} course on {platform_name}.").format(
            course_title=context['accomplishment_copy_course_name'],
            platform_name=platform_name
        )
    )
    context['twitter_share_enabled'] = share_settings.get('CERTIFICATE_TWITTER', False)
    context['twitter_share_text'] = share_settings.get(
        'CERTIFICATE_TWITTER_TEXT',
        _("I completed a course on {platform_name}. Take a look at my certificate.").format(
            platform_name=platform_name
        )
    )

    share_url = request.build_absolute_uri(
        reverse(
            'certificates:html_view',
            kwargs=dict(user_id=str(user.id), course_id=unicode(course.id))
        )
    )
    context['share_url'] = share_url
    twitter_url = ''
    if context.get('twitter_share_enabled', False):
        twitter_url = 'https://twitter.com/intent/tweet?text={twitter_share_text}&url={share_url}'.format(
            twitter_share_text=smart_str(context['twitter_share_text']),
            share_url=urllib.quote_plus(smart_str(share_url))
        )
    context['twitter_url'] = twitter_url
    context['linked_in_url'] = None
    # If enabled, show the LinkedIn "add to profile" button
    # Clicking this button sends the user to LinkedIn where they
    # can add the certificate information to their profile.
    linkedin_config = LinkedInAddToProfileConfiguration.current()

    # posting certificates to LinkedIn is not currently
    # supported in microsites/White Labels
    if linkedin_config.enabled and not microsite.is_request_in_microsite():
        context['linked_in_url'] = linkedin_config.add_to_profile_url(
            course.id,
            course.display_name,
            user_certificate.mode,
            smart_str(request.build_absolute_uri(get_certificate_url(
                user_id=user.id,
                course_id=unicode(course.id)
            )))
        )
예제 #17
0
def send_refund_notification(course_enrollment, refund_ids):
    """ Notify the support team of the refund request. """

    tags = ['auto_refund']

    if microsite.is_request_in_microsite():
        # this is not presently supported with the external service.
        raise NotImplementedError("Unable to send refund processing emails to microsite teams.")

    student = course_enrollment.user
    subject = _("[Refund] User-Requested Refund")
    body = generate_refund_notification_body(student, refund_ids)
    requester_name = student.profile.name or student.username
    create_zendesk_ticket(requester_name, student.email, subject, body, tags)
예제 #18
0
def send_refund_notification(course_enrollment, refund_ids):
    """ Notify the support team of the refund request. """

    tags = ["auto_refund"]

    if microsite.is_request_in_microsite():
        # this is not presently supported with the external service.
        raise NotImplementedError("Unable to send refund processing emails to microsite teams.")

    student = course_enrollment.user
    subject = _("[Refund] User-Requested Refund")
    body = generate_refund_notification_body(student, refund_ids)
    requester_name = student.profile.name or student.username
    create_zendesk_ticket(requester_name, student.email, subject, body, tags)
예제 #19
0
def is_comprehensive_theming_enabled():
    """
    Returns boolean indicating whether comprehensive theming functionality is enabled or disabled.
    Example:
        >> is_comprehensive_theming_enabled()
        True

    Returns:
         (bool): True if comprehensive theming is enabled else False
    """
    # Disable theming for microsites
    if microsite.is_request_in_microsite():
        return False

    return settings.ENABLE_COMPREHENSIVE_THEMING
예제 #20
0
def is_comprehensive_theming_enabled():
    """
    Returns boolean indicating whether comprehensive theming functionality is enabled or disabled.
    Example:
        >> is_comprehensive_theming_enabled()
        True

    Returns:
         (bool): True if comprehensive theming is enabled else False
    """
    # Disable theming for microsites
    if microsite.is_request_in_microsite():
        return False

    return settings.ENABLE_COMPREHENSIVE_THEMING
예제 #21
0
def _update_social_context(request, context, course, user, user_certificate,
                           platform_name):
    """
    Updates context dictionary with info required for social sharing.
    """
    share_settings = getattr(settings, 'SOCIAL_SHARING_SETTINGS', {})
    context['facebook_share_enabled'] = share_settings.get(
        'CERTIFICATE_FACEBOOK', False)
    context['facebook_app_id'] = getattr(settings, "FACEBOOK_APP_ID", None)
    context['facebook_share_text'] = share_settings.get(
        'CERTIFICATE_FACEBOOK_TEXT',
        _("I completed the {course_title} course on {platform_name}.").format(
            course_title=context['accomplishment_copy_course_name'],
            platform_name=platform_name))
    context['twitter_share_enabled'] = share_settings.get(
        'CERTIFICATE_TWITTER', False)
    context['twitter_share_text'] = share_settings.get(
        'CERTIFICATE_TWITTER_TEXT',
        _("I completed a course on {platform_name}. Take a look at my certificate."
          ).format(platform_name=platform_name))

    share_url = request.build_absolute_uri(
        reverse('certificates:html_view',
                kwargs=dict(user_id=str(user.id),
                            course_id=unicode(course.id))))
    context['share_url'] = share_url
    twitter_url = ''
    if context.get('twitter_share_enabled', False):
        twitter_url = 'https://twitter.com/intent/tweet?text={twitter_share_text}&url={share_url}'.format(
            twitter_share_text=smart_str(context['twitter_share_text']),
            share_url=urllib.quote_plus(smart_str(share_url)))
    context['twitter_url'] = twitter_url
    context['linked_in_url'] = None
    # If enabled, show the LinkedIn "add to profile" button
    # Clicking this button sends the user to LinkedIn where they
    # can add the certificate information to their profile.
    linkedin_config = LinkedInAddToProfileConfiguration.current()

    # posting certificates to LinkedIn is not currently
    # supported in microsites/White Labels
    if linkedin_config.enabled and not microsite.is_request_in_microsite():
        context['linked_in_url'] = linkedin_config.add_to_profile_url(
            course.id, course.display_name, user_certificate.mode,
            smart_str(
                request.build_absolute_uri(
                    get_certificate_url(user_id=user.id,
                                        course_id=unicode(course.id)))))
예제 #22
0
def get_themed_template_path(relative_path, default_path, **kwargs):
    """
    This is a proxy function to hide microsite_configuration behind comprehensive theming.

    The workflow considers the "Stanford theming" feature alongside of microsites.  It returns
    the path of the themed template (i.e. relative_path) if Stanford theming is enabled AND
    microsite theming is disabled, otherwise it will return the path of either the microsite
    override template or the base lms template.

    :param relative_path: relative path of themed template
    :param default_path: relative path of the microsite's or lms template to use if
        theming is disabled or microsite is enabled
    """
    is_stanford_theming_enabled = settings.FEATURES.get("USE_CUSTOM_THEME", False)
    is_microsite = microsite.is_request_in_microsite()
    if is_stanford_theming_enabled and not is_microsite:
        return relative_path
    return microsite.get_template_path(default_path, **kwargs)
예제 #23
0
def get_themed_template_path(relative_path, default_path, **kwargs):
    """
    This is a proxy function to hide microsite_configuration behind comprehensive theming.

    The workflow considers the "Stanford theming" feature alongside of microsites.  It returns
    the path of the themed template (i.e. relative_path) if Stanford theming is enabled AND
    microsite theming is disabled, otherwise it will return the path of either the microsite
    override template or the base lms template.

    :param relative_path: relative path of themed template
    :param default_path: relative path of the microsite's or lms template to use if
        theming is disabled or microsite is enabled
    """
    is_stanford_theming_enabled = settings.FEATURES.get("USE_CUSTOM_THEME", False)
    is_microsite = microsite.is_request_in_microsite()
    if is_stanford_theming_enabled and not is_microsite:
        return relative_path
    return microsite.get_template_path(default_path, **kwargs)
예제 #24
0
def is_comprehensive_theming_enabled():
    """
    Returns boolean indicating whether comprehensive theming functionality is enabled or disabled.
    Example:
        >> is_comprehensive_theming_enabled()
        True

    Returns:
         (bool): True if comprehensive theming is enabled else False
    """
    # We need to give priority to theming over microsites
    if settings.ENABLE_COMPREHENSIVE_THEMING and current_request_has_associated_site_theme():
        return True

    # Disable theming for microsites
    # Microsite configurations take priority over the default site theme.
    if microsite.is_request_in_microsite():
        return False

    return settings.ENABLE_COMPREHENSIVE_THEMING
예제 #25
0
def is_comprehensive_theming_enabled():
    """
    Returns boolean indicating whether comprehensive theming functionality is enabled or disabled.
    Example:
        >> is_comprehensive_theming_enabled()
        True

    Returns:
         (bool): True if comprehensive theming is enabled else False
    """
    # We need to give priority to theming over microsites
    if settings.ENABLE_COMPREHENSIVE_THEMING and current_request_has_associated_site_theme():
        return True

    # Disable theming for microsites
    # Microsite configurations take priority over the default site theme.
    if microsite.is_request_in_microsite():
        return False

    return settings.ENABLE_COMPREHENSIVE_THEMING
예제 #26
0
def is_request_in_themed_site():
    """
    This is a proxy function to hide microsite_configuration behind comprehensive theming.
    """
    return microsite.is_request_in_microsite()
예제 #27
0
 def test_is_request_in_microsite(self):
     """
     Tests microsite.is_request_in_microsite works as expected.
     """
     microsite.set_by_domain(self.microsite_subdomain)
     self.assertTrue(microsite.is_request_in_microsite())
예제 #28
0
def is_request_in_themed_site():
    """
    This is a proxy function to hide microsite_configuration behind comprehensive theming.
    """
    return microsite.is_request_in_microsite()
예제 #29
0
def login_and_registration_form(request, initial_mode="login"):
    """Render the combined login/registration form, defaulting to login

    This relies on the JS to asynchronously load the actual form from
    the user_api.

    Keyword Args:
        initial_mode (string): Either "login" or "register".

    """
    # Determine the URL to redirect to following login/registration/third_party_auth

    redirect_to = get_next_url_for_login_page(request)

    # If we're already logged in, redirect to the dashboard
    if request.user.is_authenticated():
        return redirect(redirect_to)

    if settings.FEATURES.get("ENABLE_EDRAAK_LOGISTRATION", False):
        redirect_to = get_next_url_for_progs_login_page(request, initial_mode)
        return redirect(redirect_to)

    # Retrieve the form descriptions from the user API
    form_descriptions = _get_form_descriptions(request)

    # If this is a microsite, revert to the old login/registration pages.
    # We need to do this for now to support existing themes.
    # Microsites can use the new logistration page by setting
    # 'ENABLE_COMBINED_LOGIN_REGISTRATION' in their microsites configuration file.
    if microsite.is_request_in_microsite() and not microsite.get_value(
            'ENABLE_COMBINED_LOGIN_REGISTRATION', False):
        if initial_mode == "login":
            return old_login_view(request)
        elif initial_mode == "register":
            return old_register_view(request)

    # Allow external auth to intercept and handle the request
    ext_auth_response = _external_auth_intercept(request, initial_mode)
    if ext_auth_response is not None:
        return ext_auth_response

    # Our ?next= URL may itself contain a parameter 'tpa_hint=x' that we need to check.
    # If present, we display a login page focused on third-party auth with that provider.
    third_party_auth_hint = None
    if '?' in redirect_to:
        try:
            next_args = urlparse.parse_qs(urlparse.urlparse(redirect_to).query)
            provider_id = next_args['tpa_hint'][0]
            if third_party_auth.provider.Registry.get(provider_id=provider_id):
                third_party_auth_hint = provider_id
                initial_mode = "hinted_login"
        except (KeyError, ValueError, IndexError):
            pass

    # Otherwise, render the combined login/registration page
    context = {
        'data': {
            'login_redirect_url':
            redirect_to,
            'initial_mode':
            initial_mode,
            'third_party_auth':
            _third_party_auth_context(request, redirect_to),
            'third_party_auth_hint':
            third_party_auth_hint or '',
            'platform_name':
            settings.PLATFORM_NAME,

            # Include form descriptions retrieved from the user API.
            # We could have the JS client make these requests directly,
            # but we include them in the initial page load to avoid
            # the additional round-trip to the server.
            'login_form_desc':
            json.loads(form_descriptions['login']),
            'registration_form_desc':
            json.loads(form_descriptions['registration']),
            'password_reset_form_desc':
            json.loads(form_descriptions['password_reset']),
        },
        'login_redirect_url':
        redirect_to,  # This gets added to the query string of the "Sign In" button in header
        'responsive': True,
        'allow_iframing': True,
        'disable_courseware_js': True,
        'disable_footer': True,
    }

    # Edraak: Check if the origin is a safe (trusted) url. if so pass it
    # to the front end to allow navigating back to the originating
    # site after authentication.
    origin_url = request.GET.get('origin')
    if origin_url and is_origin_url_allowed(origin_url):
        context['data']['origin'] = origin_url

    return render_to_response('student_account/login_and_register.html',
                              context)
예제 #30
0
def render_html_view(request, user_id, course_id):
    """
    This public view generates an HTML representation of the specified student's certificate
    If a certificate is not available, we display a "Sorry!" screen instead
    """

    # Create the initial view context, bootstrapping with Django settings and passed-in values
    context = {}
    context['platform_name'] = microsite.get_value("platform_name", settings.PLATFORM_NAME)
    context['course_id'] = course_id
    preview_mode = request.GET.get('preview', None)

    # Update the view context with the default ConfigurationModel settings
    configuration = CertificateHtmlViewConfiguration.get_config()
    # if we are in a microsite, then let's first see if there is an override
    # section in our config
    config_key = microsite.get_value('microsite_config_key', 'default')
    # if there is no special microsite override, then let's use default
    if config_key not in configuration:
        config_key = 'default'
    context.update(configuration.get(config_key, {}))

    # Translators:  'All rights reserved' is a legal term used in copyrighting to protect published content
    reserved = _("All rights reserved")
    context['copyright_text'] = '&copy; {year} {platform_name}. {reserved}.'.format(
        year=settings.COPYRIGHT_YEAR,
        platform_name=context.get('platform_name'),
        reserved=reserved
    )

    # Translators:  This text is bound to the HTML 'title' element of the page and appears
    # in the browser title bar when a requested certificate is not found or recognized
    context['document_title'] = _("Invalid Certificate")

    # Translators: The &amp; characters represent an ampersand character and can be ignored
    context['company_tos_urltext'] = _("Terms of Service &amp; Honor Code")

    # Translators: A 'Privacy Policy' is a legal document/statement describing a website's use of personal information
    context['company_privacy_urltext'] = _("Privacy Policy")

    # Translators: This line appears as a byline to a header image and describes the purpose of the page
    context['logo_subtitle'] = _("Certificate Validation")
    invalid_template_path = 'certificates/invalid.html'

    # Kick the user back to the "Invalid" screen if the feature is disabled
    if not has_html_certificates_enabled(course_id):
        return render_to_response(invalid_template_path, context)

    # Load the core building blocks for the view context
    try:
        course_key = CourseKey.from_string(course_id)
        user = User.objects.get(id=user_id)
        course = modulestore().get_course(course_key)

        if not course:
            raise CourseDoesNotExist

        # Attempt to load the user's generated certificate data
        if preview_mode:
            user_certificate = GeneratedCertificate.objects.get(
                user=user,
                course_id=course_key,
                mode=preview_mode
            )
        else:
            user_certificate = GeneratedCertificate.objects.get(
                user=user,
                course_id=course_key
            )

    # If there's no generated certificate data for this user, we need to see if we're in 'preview' mode...
    # If we are, we'll need to create a mock version of the user_certificate container for previewing
    except GeneratedCertificate.DoesNotExist:
        if preview_mode and (
            has_access(request.user, 'instructor', course)
            or has_access(request.user, 'staff', course)
        ):
            user_certificate = GeneratedCertificate(
                mode=preview_mode,
                verify_uuid=unicode(uuid4().hex),
                modified_date=datetime.now().date()
            )
        else:
            return render_to_response(invalid_template_path, context)

    # For any other expected exceptions, kick the user back to the "Invalid" screen
    except (InvalidKeyError, CourseDoesNotExist, User.DoesNotExist):
        return render_to_response(invalid_template_path, context)

    # Badge Request Event Tracking Logic
    if 'evidence_visit' in request.GET:
        try:
            badge = BadgeAssertion.objects.get(user=user, course_id=course_key)
            tracker.emit(
                'edx.badge.assertion.evidence_visited',
                {
                    'user_id': user.id,
                    'course_id': unicode(course_key),
                    'enrollment_mode': badge.mode,
                    'assertion_id': badge.id,
                    'assertion_image_url': badge.data['image'],
                    'assertion_json_url': badge.data['json']['id'],
                    'issuer': badge.data['issuer'],
                }
            )
        except BadgeAssertion.DoesNotExist:
            log.warn(
                "Could not find badge for %s on course %s.",
                user.id,
                course_key,
            )

    # Okay, now we have all of the pieces, time to put everything together

    # Get the active certificate configuration for this course
    # If we do not have an active certificate, we'll need to send the user to the "Invalid" screen
    # Passing in the 'preview' parameter, if specified, will return a configuration, if defined
    active_configuration = get_active_web_certificate(course, preview_mode)
    if active_configuration is None:
        return render_to_response(invalid_template_path, context)
    else:
        context['certificate_data'] = active_configuration

    # Append/Override the existing view context values with any mode-specific ConfigurationModel values
    context.update(configuration.get(user_certificate.mode, {}))

    # Append/Override the existing view context values with request-time values
    _update_certificate_context(context, course, user, user_certificate)
    share_url = request.build_absolute_uri(
        reverse(
            'certificates:html_view',
            kwargs=dict(user_id=str(user_id), course_id=unicode(course_id))
        )
    )
    context['share_url'] = share_url
    twitter_url = ''
    if context.get('twitter_share_enabled', False):
        twitter_url = 'https://twitter.com/intent/tweet?text={twitter_share_text}&url={share_url}'.format(
            twitter_share_text=smart_str(context['twitter_share_text']),
            share_url=urllib.quote_plus(smart_str(share_url))
        )
    context['twitter_url'] = twitter_url
    context['full_course_image_url'] = request.build_absolute_uri(course_image_url(course))

    # If enabled, show the LinkedIn "add to profile" button
    # Clicking this button sends the user to LinkedIn where they
    # can add the certificate information to their profile.
    linkedin_config = LinkedInAddToProfileConfiguration.current()

    # posting certificates to LinkedIn is not currently
    # supported in microsites/White Labels
    if linkedin_config.enabled and not microsite.is_request_in_microsite():
        context['linked_in_url'] = linkedin_config.add_to_profile_url(
            course.id,
            course.display_name,
            user_certificate.mode,
            smart_str(request.build_absolute_uri(get_certificate_url(
                user_id=user.id,
                course_id=unicode(course.id)
            )))
        )
    else:
        context['linked_in_url'] = None

    # Microsites will need to be able to override any hard coded
    # content that was put into the context in the
    # _update_certificate_context() call above. For example the
    # 'company_about_description' talks about edX, which we most likely
    # do not want to keep in a microsite
    #
    # So we need to re-apply any configuration/content that
    # we are sourceing from the database. This is somewhat duplicative of
    # the code at the beginning of this method, but we
    # need the configuration at the top as some error code paths
    # require that to be set up early on in the pipeline
    #
    microsite_config_key = microsite.get_value('microsite_config_key')
    if microsite_config_key:
        context.update(configuration.get(microsite_config_key, {}))

    # track certificate evidence_visited event for analytics when certificate_user and accessing_user are different
    if request.user and request.user.id != user.id:
        emit_certificate_event('evidence_visited', user, course_id, course, {
            'certificate_id': user_certificate.verify_uuid,
            'enrollment_mode': user_certificate.mode,
            'social_network': CertificateSocialNetworks.linkedin
        })

    # Append/Override the existing view context values with any course-specific static values from Advanced Settings
    context.update(course.cert_html_view_overrides)

    # FINALLY, generate and send the output the client
    if settings.FEATURES.get('CUSTOM_CERTIFICATE_TEMPLATES_ENABLED', False):
        custom_template = get_certificate_template(course_key, user_certificate.mode)
        if custom_template:
            template = Template(
                custom_template,
                output_encoding='utf-8',
                input_encoding='utf-8',
                default_filters=['decode.utf8'],
                encoding_errors='replace',
            )
            context = RequestContext(request, context)
            return HttpResponse(template.render(context))

    return render_to_response("certificates/valid.html", context)
예제 #31
0
def login_and_registration_form(request, initial_mode="login"):
    """Render the combined login/registration form, defaulting to login

    This relies on the JS to asynchronously load the actual form from
    the user_api.

    Keyword Args:
        initial_mode (string): Either "login" or "register".

    """
    # Determine the URL to redirect to following login/registration/third_party_auth
    redirect_to = get_next_url_for_login_page(request)

    # If we're already logged in, redirect to the dashboard
    if request.user.is_authenticated():
        return redirect(redirect_to)

    # Retrieve the form descriptions from the user API
    form_descriptions = _get_form_descriptions(request)

    # If this is a microsite, revert to the old login/registration pages.
    # We need to do this for now to support existing themes.
    # Microsites can use the new logistration page by setting
    # 'ENABLE_COMBINED_LOGIN_REGISTRATION' in their microsites configuration file.
    if microsite.is_request_in_microsite() and not microsite.get_value('ENABLE_COMBINED_LOGIN_REGISTRATION', False):
        if initial_mode == "login":
            return old_login_view(request)
        elif initial_mode == "register":
            return old_register_view(request)

    # Allow external auth to intercept and handle the request
    ext_auth_response = _external_auth_intercept(request, initial_mode)
    if ext_auth_response is not None:
        return ext_auth_response

    # Our ?next= URL may itself contain a parameter 'tpa_hint=x' that we need to check.
    # If present, we display a login page focused on third-party auth with that provider.
    third_party_auth_hint = None
    if '?' in redirect_to:
        try:
            next_args = urlparse.parse_qs(urlparse.urlparse(redirect_to).query)
            provider_id = next_args['tpa_hint'][0]
            if third_party_auth.provider.Registry.get(provider_id=provider_id):
                third_party_auth_hint = provider_id
                initial_mode = "hinted_login"
        except (KeyError, ValueError, IndexError):
            pass

    # Otherwise, render the combined login/registration page
    context = {
        'login_redirect_url': redirect_to,  # This gets added to the query string of the "Sign In" button in the header
        'disable_courseware_js': True,
        'initial_mode': initial_mode,
        'third_party_auth': json.dumps(_third_party_auth_context(request, redirect_to)),
        'third_party_auth_hint': third_party_auth_hint or '',
        'platform_name': settings.PLATFORM_NAME,
        'responsive': True,

        # Include form descriptions retrieved from the user API.
        # We could have the JS client make these requests directly,
        # but we include them in the initial page load to avoid
        # the additional round-trip to the server.
        'login_form_desc': form_descriptions['login'],
        'registration_form_desc': form_descriptions['registration'],
        'password_reset_form_desc': form_descriptions['password_reset'],
    }

    return render_to_response('student_account/login_and_register.html', context)
예제 #32
0
def open_source_footer_context_processor(request):
    """
    Checks the site name to determine whether to use the edX.org footer or the Open Source Footer.
    """
    return dict(
        [
            ("IS_EDX_DOMAIN", settings.FEATURES.get('IS_EDX_DOMAIN', False)),
            ("ENABLE_CUSTOM_THEME", settings.FEATURES.get("USE_CUSTOM_THEME", False) and not microsite.is_request_in_microsite())
        ]
    )
예제 #33
0
def is_request_in_themed_site():
    """
    This is a proxy function to hide microsite_configuration behind comprehensive theming.
    """
    # We need to give priority to theming/site-configuration over microsites
    return configuration_helpers.is_site_configuration_enabled() or microsite.is_request_in_microsite()
예제 #34
0
    def student_view(self, context=None, authoring=False):
        scheme = 'https' if settings.HTTPS == 'on' else 'http'
        lms_base = settings.ENV_TOKENS.get('LMS_BASE')
        if isinstance(context, QueryDict):
            context = context.dict()

        if microsite.is_request_in_microsite():
            subdomain = microsite.get_value(
                "domain_prefix",
                None) or microsite.get_value('microsite_config_key')
            lms_base = "{}.{}".format(subdomain, lms_base)
        scorm_player_url = ""

        course_directory = self.scorm_file
        if self.scorm_player == 'SCORM_PKG_INTERNAL':
            # TODO: support initial filename other than index.html for internal players
            #scorm_player_url = '{}://{}{}'.format(scheme, lms_base, self.scorm_file)
            scorm_player_url = '{}{}'.format(self.scorm_file, '/index.html')
        elif self.scorm_player:
            player_config = DEFINED_PLAYERS[self.scorm_player]
            player = player_config['location']
            if '://' in player:
                scorm_player_url = player
            else:
                scorm_player_url = '{}://{}{}'.format(scheme, lms_base, player)
            course_directory = '{}://{}{}'.format(
                scheme, lms_base,
                self.runtime.handler_url(self, "proxy_content"))

        html = self.resource_string("static/html/scormxblock.html")

        # don't call handlers if student_view is not called from within LMS
        # (not really a student)
        if not authoring:
            get_url = '{}://{}{}'.format(
                scheme, lms_base,
                self.runtime.handler_url(self, "get_raw_scorm_status"))
            set_url = '{}://{}{}'.format(
                scheme, lms_base,
                self.runtime.handler_url(self, "set_raw_scorm_status"))
        # PreviewModuleSystem (runtime Mixin from Studio) won't have a hostname
        else:
            # we don't want to get/set SCORM status from preview
            get_url = set_url = '#'

        # if display type is popup, don't use the full window width for the host iframe
        iframe_width = self.display_type == 'popup' and DEFAULT_IFRAME_WIDTH or self.display_width
        iframe_height = self.display_type == 'popup' and DEFAULT_IFRAME_HEIGHT or self.display_height

        try:
            player_config = json.loads(self.player_configuration)
        except ValueError:
            player_config = {}

        frag = Fragment()
        frag.add_content(
            MakoTemplate(text=html.format(
                self=self,
                scorm_player_url=scorm_player_url,
                get_url=get_url,
                set_url=set_url,
                iframe_width=iframe_width,
                iframe_height=iframe_height,
                player_config=player_config,
                scorm_file=course_directory)).render_unicode())

        frag.add_css(self.resource_string("static/css/scormxblock.css"))
        context['block_id'] = self.url_name
        js = self.resource_string("static/js/src/scormxblock.js")
        jsfrag = MakoTemplate(js).render_unicode(**context)
        frag.add_javascript(jsfrag)

        # TODO: this will only work to display staff debug info if 'scormxblock' is one of the
        # categories of blocks that are specified in lms/templates/staff_problem_info.html so this will
        # for now have to be overridden in theme or directly in edx-platform
        # TODO: is there another way to approach this?  key's location.category isn't mutable to spoof 'problem',
        # like setting the name in the entry point to 'problem'.  Doesn't seem like a good idea.  Better to
        # have 'staff debuggable' categories configurable in settings or have an XBlock declare itself staff debuggable
        if SCORM_DISPLAY_STAFF_DEBUG_INFO and not authoring:  # don't show for author preview
            from courseware.access import has_access
            from courseware.courses import get_course_by_id

            course = get_course_by_id(self.xmodule_runtime.course_id)
            dj_user = self.xmodule_runtime._services['user']._django_user
            has_instructor_access = bool(
                has_access(dj_user, 'instructor', course))
            if has_instructor_access:
                disable_staff_debug_info = settings.FEATURES.get(
                    'DISPLAY_DEBUG_INFO_TO_STAFF', True) and False or True
                block = self
                view = 'student_view'
                frag = add_staff_markup(dj_user, has_instructor_access,
                                        disable_staff_debug_info, block, view,
                                        frag, context)

        frag.initialize_js('ScormXBlock_{0}'.format(context['block_id']))
        return frag
예제 #35
0
def login_and_registration_form(request, initial_mode="login"):
    """Render the combined login/registration form, defaulting to login

    This relies on the JS to asynchronously load the actual form from
    the user_api.

    Keyword Args:
        initial_mode (string): Either "login" or "register".

    """
    # Determine the URL to redirect to following login/registration/third_party_auth
    redirect_to = get_next_url_for_login_page(request)

    # If we're already logged in, redirect to the dashboard
    if request.user.is_authenticated():
        return redirect(redirect_to)

    # Retrieve the form descriptions from the user API
    form_descriptions = _get_form_descriptions(request)

    # If this is a microsite, revert to the old login/registration pages.
    # We need to do this for now to support existing themes.
    if microsite.is_request_in_microsite():
        if initial_mode == "login":
            return old_login_view(request)
        elif initial_mode == "register":
            return old_register_view(request)

    # Allow external auth to intercept and handle the request
    ext_auth_response = _external_auth_intercept(request, initial_mode)
    if ext_auth_response is not None:
        return ext_auth_response

    # Our ?next= URL may itself contain a parameter 'tpa_hint=x' that we need to check.
    # If present, we display a login page focused on third-party auth with that provider.
    third_party_auth_hint = None
    if '?' in redirect_to:
        try:
            next_args = urlparse.parse_qs(urlparse.urlparse(redirect_to).query)
            provider_id = next_args['tpa_hint'][0]
            if third_party_auth.provider.Registry.get(provider_id=provider_id):
                third_party_auth_hint = provider_id
                initial_mode = "hinted_login"
        except (KeyError, ValueError, IndexError):
            pass

    # Otherwise, render the combined login/registration page
    context = {
        'login_redirect_url':
        redirect_to,  # This gets added to the query string of the "Sign In" button in the header
        'disable_courseware_js':
        True,
        'initial_mode':
        initial_mode,
        'third_party_auth':
        json.dumps(_third_party_auth_context(request, redirect_to)),
        'third_party_auth_hint':
        third_party_auth_hint or '',
        'platform_name':
        settings.PLATFORM_NAME,
        'responsive':
        True,

        # Include form descriptions retrieved from the user API.
        # We could have the JS client make these requests directly,
        # but we include them in the initial page load to avoid
        # the additional round-trip to the server.
        'login_form_desc':
        form_descriptions['login'],
        'registration_form_desc':
        form_descriptions['registration'],
        'password_reset_form_desc':
        form_descriptions['password_reset'],
    }

    return render_to_response('student_account/login_and_register.html',
                              context)