Exemplo n.º 1
0
def get_logo_url():
    """
    Return the url for the branded logo image to be used
    """

    # if the MicrositeConfiguration has a value for the logo_image_url
    # let's use that
    image_url = microsite.get_value('logo_image_url')
    if image_url:
        return '{static_url}{image_url}'.format(
            static_url=settings.STATIC_URL,
            image_url=image_url
        )

    # otherwise, use the legacy means to configure this
    university = microsite.get_value('university')

    if university is None:
        return '{static_url}images/header-logo.png'.format(
            static_url=settings.STATIC_URL
        )

    return '{static_url}images/{uni}-on-edx-logo.png'.format(
        static_url=settings.STATIC_URL, uni=university
    )
Exemplo n.º 2
0
def view_student_survey(user, survey_name, course=None, redirect_url=None, is_required=False, skip_redirect_url=None):
    """
    Shared utility method to render a survey form
    NOTE: This method is shared between the Survey and Courseware Djangoapps
    """

    redirect_url = redirect_url if redirect_url else reverse('dashboard')
    dashboard_redirect_url = reverse('dashboard')
    skip_redirect_url = skip_redirect_url if skip_redirect_url else dashboard_redirect_url

    survey = SurveyForm.get(survey_name, throw_if_not_found=False)
    if not survey:
        return HttpResponseRedirect(redirect_url)

    # the result set from get_answers, has an outer key with the user_id
    # just remove that outer key to make the JSON payload simplier
    existing_answers = survey.get_answers(user=user).get(user.id, {})

    platform_name = microsite.get_value('platform_name', settings.PLATFORM_NAME)

    context = {
        'existing_data_json': json.dumps(existing_answers),
        'postback_url': reverse('submit_answers', args=[survey_name]),
        'redirect_url': redirect_url,
        'skip_redirect_url': skip_redirect_url,
        'dashboard_redirect_url': dashboard_redirect_url,
        'survey_form': survey.form,
        'is_required': is_required,
        'mail_to_link': microsite.get_value('email_from_address', settings.CONTACT_EMAIL),
        'platform_name': platform_name,
        'course': course,
    }

    return render_to_response("survey/survey.html", context)
Exemplo n.º 3
0
def get_visible_courses():
    """
    Return the set of CourseDescriptors that should be visible in this branded instance
    """

    filtered_by_org = microsite.get_value('course_org_filter')

    _courses = modulestore().get_courses(org=filtered_by_org)

    courses = [c for c in _courses
               if isinstance(c, CourseDescriptor)]
    courses = sorted(courses, key=lambda course: course.number)

    subdomain = microsite.get_value('subdomain', 'default')

    # See if we have filtered course listings in this domain
    filtered_visible_ids = None

    # this is legacy format which is outside of the microsite feature -- also handle dev case, which should not filter
    if hasattr(settings, 'COURSE_LISTINGS') and subdomain in settings.COURSE_LISTINGS and not settings.DEBUG:
        filtered_visible_ids = frozenset([SlashSeparatedCourseKey.from_deprecated_string(c) for c in settings.COURSE_LISTINGS[subdomain]])

    if filtered_by_org:
        return [course for course in courses if course.location.org == filtered_by_org]
    if filtered_visible_ids:
        return [course for course in courses if course.id in filtered_visible_ids]
    else:
        # Let's filter out any courses in an "org" that has been declared to be
        # in a Microsite
        org_filter_out_set = microsite.get_all_orgs()
        return [course for course in courses if course.location.org not in org_filter_out_set]
Exemplo n.º 4
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)
Exemplo n.º 5
0
 def __getattr__(self, name):
     try:
         if isinstance(microsite.get_value(name), dict):
             return microsite.get_dict(name, getattr(base_settings, name))
         return microsite.get_value(name, getattr(base_settings, name))
     except KeyError:
         return getattr(base_settings, name)
Exemplo n.º 6
0
def show_cart(request):
    """
    This view shows cart items.
    """
    cart = Order.get_cart_for_user(request.user)
    is_any_course_expired, expired_cart_items, expired_cart_item_names, valid_cart_item_tuples = \
        verify_for_closed_enrollment(request.user, cart)
    site_name = microsite.get_value('SITE_NAME', settings.SITE_NAME)

    if is_any_course_expired:
        for expired_item in expired_cart_items:
            Order.remove_cart_item_from_order(expired_item, request.user)
        cart.update_order_type()

    callback_url = request.build_absolute_uri(
        reverse("shoppingcart.views.postpay_callback")
    )
    form_html = render_purchase_form_html(cart, callback_url=callback_url)
    context = {
        'order': cart,
        'shoppingcart_items': valid_cart_item_tuples,
        'amount': cart.total_cost,
        'is_course_enrollment_closed': is_any_course_expired,
        'expired_course_names': expired_cart_item_names,
        'site_name': site_name,
        'form_html': form_html,
        'currency_symbol': settings.PAID_COURSE_REGISTRATION_CURRENCY[1],
        'currency': settings.PAID_COURSE_REGISTRATION_CURRENCY[0],
        'enable_bulk_purchase': microsite.get_value('ENABLE_SHOPPING_CART_BULK_PURCHASE', True)
    }
    return render_to_response("shoppingcart/shopping_cart.html", context)
Exemplo n.º 7
0
def send_mail_to_student(student, param_dict):
    """
    Check parameters, set text template and send email to student
    """
    if "course" in param_dict:
        param_dict["course_name"] = param_dict["course"].display_name

    param_dict["site_name"] = microsite.get_value("SITE_NAME", param_dict["site_name"])

    subject = None
    message = None

    message_type = param_dict["message"]

    email_template_dict = {
        "allowed_enroll": ("ccx/enroll_email_allowedsubject.txt", "ccx/enroll_email_allowedmessage.txt"),
        "enrolled_enroll": ("ccx/enroll_email_enrolledsubject.txt", "ccx/enroll_email_enrolledmessage.txt"),
        "allowed_unenroll": ("ccx/unenroll_email_subject.txt", "ccx/unenroll_email_allowedmessage.txt"),
        "enrolled_unenroll": ("ccx/unenroll_email_subject.txt", "ccx/unenroll_email_enrolledmessage.txt"),
    }

    subject_template, message_template = email_template_dict.get(message_type, (None, None))
    if subject_template is not None and message_template is not None:
        subject = render_to_string(subject_template, param_dict)
        message = render_to_string(message_template, param_dict)

    if subject and message:
        message = message.strip()

        subject = "".join(subject.splitlines())
        from_address = microsite.get_value("email_from_address", settings.DEFAULT_FROM_EMAIL)

        send_mail(subject, message, from_address, [student], fail_silently=False)
Exemplo n.º 8
0
def handle_va_enrollment_event(sender, student, **kwargs):
    """
    set Marketo VA Learning Path Enrolled for Lead corresponding to user
    """
    if not (get_value("course_enable_marketo_integration", None) and not \
            getattr(settings.FEATURES, "COURSE_ENABLE_MARKETO_INTEGRATION", None)
            ):
        return

    logger.info(('Setting VA Learning Path Enrolled and edX registered for Lead with email {0}.').format(student.email))
    mkto_field_id_va = get_value("marketo_va_enrollment_field_id", None)
    mkto_field_id_edx = get_value("marketo_edx_enrollment_field_id", None)
    if not mkto_field_id_va:
        logger.warn(('Can\'t set VA Learning Path Enrolled for Lead with email {0}.').format(student.email))
    if not mkto_field_id_edx:
        logger.warn(('Can\'t set edX Registered for Lead with email {0}.').format(student.email))

    try:
        mc = get_marketo_client()
        status = mc.execute(method='update_lead', lookupField='email',
                            lookupValue=student.email,
                            values={mkto_field_id_va: True,
                                    mkto_field_id_edx: True})
        if status != 'updated':
            raise MarketoException({'message': "Update failed with status {0}".format(status), 'code': None})

    except MarketoException as e:
        logger.warn(('Can\'t set VA Learning Path Enrolled or edX Registered for Lead with email {0}.').format(student.email))
Exemplo n.º 9
0
def get_logo_url():
    """
    Return the url for the branded logo image to be used
    """

    # if the MicrositeConfiguration has a value for the logo_image_url
    # let's use that
    image_url = microsite.get_value('logo_image_url')
    if image_url:
        return '{static_url}{image_url}'.format(
            static_url=settings.STATIC_URL,
            image_url=image_url
        )

    # otherwise, use the legacy means to configure this
    university = microsite.get_value('university')

    if university is None and settings.FEATURES.get('IS_EDX_DOMAIN', False):
        return '{static_url}images/edx-theme/edx-logo-77x36.png'.format(
            static_url=settings.STATIC_URL
        )
    elif university:
        return '{static_url}images/{uni}-on-edx-logo.png'.format(
            static_url=settings.STATIC_URL, uni=university
        )
    else:
        return '{static_url}images/default-theme/logo.png'.format(
            static_url=settings.STATIC_URL
        )
Exemplo n.º 10
0
    def get_setting(self, name):
        """ Get the value of a setting, or raise KeyError """
        microsite_oauth = microsite.get_value("SOCIAL_AUTH_OAUTH_SECRETS", False)
        if microsite_oauth:
            current = microsite_oauth.get(self.backend_name, {})

            if name is "KEY":
                return current.get("KEY")
            if name is "SECRET":
                site_key = microsite.get_value("microsite_name")
                secrets = settings.MICROSITE_SECRETS.get(site_key, {}).get("SOCIAL_AUTH_OAUTH_SECRETS", {})
                return current.get("SECRET", secrets.get(self.backend_name))

        if name == "KEY":
            return self.key
        if name == "SECRET":
            if self.secret:
                return self.secret
            # To allow instances to avoid storing secrets in the DB, the secret can also be set via Django:
            return getattr(settings, "SOCIAL_AUTH_OAUTH_SECRETS", {}).get(self.backend_name, "")
        if self.other_settings:
            other_settings = json.loads(self.other_settings)
            assert isinstance(other_settings, dict), "other_settings should be a JSON object (dictionary)"
            return other_settings[name]
        raise KeyError
Exemplo n.º 11
0
def send_mail_to_student(student, param_dict):
    """
    Construct the email using templates and then send it.
    `student` is the student's email address (a `str`),

    `param_dict` is a `dict` with keys [
    `site_name`: name given to edX instance (a `str`)
    `registration_url`: url for registration (a `str`)
    `course_key`: id of course (a CourseKey)
    `auto_enroll`: user input option (a `str`)
    `course_url`: url of course (a `str`)
    `email_address`: email of student (a `str`)
    `full_name`: student full name (a `str`)
    `message`: type of email to send and template to use (a `str`)
    `is_shib_course`: (a `boolean`)
                                        ]
    Returns a boolean indicating whether the email was sent successfully.
    """

    # add some helpers and microconfig subsitutions
    if 'course' in param_dict:
        param_dict['course_name'] = param_dict['course'].display_name_with_default
    param_dict['site_name'] = microsite.get_value(
        'SITE_NAME',
        param_dict.get('site_name', '')
    )

    subject = None
    message = None

    message_type = param_dict['message']

    email_template_dict = {
        'allowed_enroll': ('emails/enroll_email_allowedsubject.txt', 'emails/enroll_email_allowedmessage.txt'),
        'enrolled_enroll': ('emails/enroll_email_enrolledsubject.txt', 'emails/enroll_email_enrolledmessage.txt'),
        'allowed_unenroll': ('emails/unenroll_email_subject.txt', 'emails/unenroll_email_allowedmessage.txt'),
        'enrolled_unenroll': ('emails/unenroll_email_subject.txt', 'emails/unenroll_email_enrolledmessage.txt'),
    }

    subject_template, message_template = email_template_dict.get(message_type, (None, None))
    if subject_template is not None and message_template is not None:
        subject = render_to_string(subject_template, param_dict)
        message = render_to_string(message_template, param_dict)

    if subject and message:
        # Remove leading and trailing whitespace from body
        message = message.strip()

        # Email subject *must not* contain newlines
        subject = ''.join(subject.splitlines())
        from_address = microsite.get_value(
            'email_from_address',
            settings.DEFAULT_FROM_EMAIL
        )

        send_mail(subject, message, from_address, [student], fail_silently=False)

        return True
    else:
        return False
Exemplo n.º 12
0
    def __init__(self, items_data, item_id, date, is_invoice, total_cost, payment_received, balance):
        """
        Accepts the following positional arguments

        items_data - A list having the following items for each row.
            item_description - String
            quantity - Integer
            list_price - float
            discount - float
            item_total - float
        id - String
        date - datetime
        is_invoice - boolean - True (for invoice) or False (for Receipt)
        total_cost - float
        payment_received - float
        balance - float
        """

        # From settings
        self.currency = settings.PAID_COURSE_REGISTRATION_CURRENCY[1]
        self.logo_path = microsite.get_value("PDF_RECEIPT_LOGO_PATH", settings.PDF_RECEIPT_LOGO_PATH)
        self.cobrand_logo_path = microsite.get_value(
            "PDF_RECEIPT_COBRAND_LOGO_PATH", settings.PDF_RECEIPT_COBRAND_LOGO_PATH
        )
        self.tax_label = microsite.get_value("PDF_RECEIPT_TAX_ID_LABEL", settings.PDF_RECEIPT_TAX_ID_LABEL)
        self.tax_id = microsite.get_value("PDF_RECEIPT_TAX_ID", settings.PDF_RECEIPT_TAX_ID)
        self.footer_text = microsite.get_value("PDF_RECEIPT_FOOTER_TEXT", settings.PDF_RECEIPT_FOOTER_TEXT)
        self.disclaimer_text = microsite.get_value("PDF_RECEIPT_DISCLAIMER_TEXT", settings.PDF_RECEIPT_DISCLAIMER_TEXT)
        self.billing_address_text = microsite.get_value(
            "PDF_RECEIPT_BILLING_ADDRESS", settings.PDF_RECEIPT_BILLING_ADDRESS
        )
        self.terms_conditions_text = microsite.get_value(
            "PDF_RECEIPT_TERMS_AND_CONDITIONS", settings.PDF_RECEIPT_TERMS_AND_CONDITIONS
        )
        self.brand_logo_height = microsite.get_value(
            "PDF_RECEIPT_LOGO_HEIGHT_MM", settings.PDF_RECEIPT_LOGO_HEIGHT_MM
        ) * mm
        self.cobrand_logo_height = microsite.get_value(
            "PDF_RECEIPT_COBRAND_LOGO_HEIGHT_MM", settings.PDF_RECEIPT_COBRAND_LOGO_HEIGHT_MM
        ) * mm

        # From Context
        self.items_data = items_data
        self.item_id = item_id
        self.date = ModuleI18nService().strftime(date, 'SHORT_DATE')
        self.is_invoice = is_invoice
        self.total_cost = '{currency}{amount:.2f}'.format(currency=self.currency, amount=total_cost)
        self.payment_received = '{currency}{amount:.2f}'.format(currency=self.currency, amount=payment_received)
        self.balance = '{currency}{amount:.2f}'.format(currency=self.currency, amount=balance)

        # initialize the pdf variables
        self.margin = 15 * mm
        self.page_width = letter[0]
        self.page_height = letter[1]
        self.min_clearance = 3 * mm
        self.second_page_available_height = ''
        self.second_page_start_y_pos = ''
        self.first_page_available_height = ''
        self.pdf = None
Exemplo n.º 13
0
def send_mail_to_student(student, param_dict):
    """
    Check parameters, set text template and send email to student
    """
    if 'course' in param_dict:
        param_dict['course_name'] = param_dict['course'].display_name

    param_dict['site_name'] = microsite.get_value(
        'SITE_NAME',
        param_dict['site_name']
    )

    subject = None
    message = None

    message_type = param_dict['message']

    email_template_dict = {
        'allowed_enroll': (
            'ccx/enroll_email_allowedsubject.txt',
            'ccx/enroll_email_allowedmessage.txt'
        ),
        'enrolled_enroll': (
            'ccx/enroll_email_enrolledsubject.txt',
            'ccx/enroll_email_enrolledmessage.txt'
        ),
        'allowed_unenroll': (
            'ccx/unenroll_email_subject.txt',
            'ccx/unenroll_email_allowedmessage.txt'
        ),
        'enrolled_unenroll': (
            'ccx/unenroll_email_subject.txt',
            'ccx/unenroll_email_enrolledmessage.txt'
        ),
    }

    subject_template, message_template = email_template_dict.get(
        message_type, (None, None)
    )
    if subject_template is not None and message_template is not None:
        subject = render_to_string(subject_template, param_dict)
        message = render_to_string(message_template, param_dict)

    if subject and message:
        message = message.strip()

        subject = ''.join(subject.splitlines())
        from_address = microsite.get_value(
            'email_from_address',
            settings.DEFAULT_FROM_EMAIL
        )

        send_mail(
            subject,
            message,
            from_address,
            [student],
            fail_silently=False
        )
Exemplo n.º 14
0
 def test_clear(self):
     """
     Tests microsite.clear works as expected.
     """
     microsite.set_by_domain(self.microsite_subdomain)
     self.assertEqual(
         microsite.get_value('platform_name'),
         'Test Microsite'
     )
     microsite.clear()
     self.assertIsNone(microsite.get_value('platform_name'))
Exemplo n.º 15
0
def checkout_receipt(request):
    """ Receipt view. """

    page_title = _('Receipt')
    is_payment_complete = True
    payment_support_email = microsite.get_value('payment_support_email', settings.PAYMENT_SUPPORT_EMAIL)
    payment_support_link = '<a href=\"mailto:{email}\">{email}</a>'.format(email=payment_support_email)

    is_cybersource = all(k in request.POST for k in ('signed_field_names', 'decision', 'reason_code'))
    if is_cybersource and request.POST['decision'] != 'ACCEPT':
        # Cybersource may redirect users to this view if it couldn't recover
        # from an error while capturing payment info.
        is_payment_complete = False
        page_title = _('Payment Failed')
        reason_code = request.POST['reason_code']
        # if the problem was with the info submitted by the user, we present more detailed messages.
        if is_user_payment_error(reason_code):
            error_summary = _("There was a problem with this transaction. You have not been charged.")
            error_text = _(
                "Make sure your information is correct, or try again with a different card or another form of payment."
            )
        else:
            error_summary = _("A system error occurred while processing your payment. You have not been charged.")
            error_text = _("Please wait a few minutes and then try again.")
        for_help_text = _("For help, contact {payment_support_link}.").format(payment_support_link=payment_support_link)
    else:
        # if anything goes wrong rendering the receipt, it indicates a problem fetching order data.
        error_summary = _("An error occurred while creating your receipt.")
        error_text = None  # nothing particularly helpful to say if this happens.
        for_help_text = _(
            "If your course does not appear on your dashboard, contact {payment_support_link}."
        ).format(payment_support_link=payment_support_link)

    commerce_configuration = CommerceConfiguration.current()
    # user order cache should be cleared when a new order is placed
    # so user can see new order in their order history.
    if is_payment_complete and commerce_configuration.enabled and commerce_configuration.is_cache_enabled:
        cache_key = commerce_configuration.CACHE_KEY + '.' + str(request.user.id)
        cache.delete(cache_key)

    context = {
        'page_title': page_title,
        'is_payment_complete': is_payment_complete,
        'platform_name': microsite.get_value('platform_name', settings.PLATFORM_NAME),
        'verified': SoftwareSecurePhotoVerification.verification_valid_or_pending(request.user).exists(),
        'error_summary': error_summary,
        'error_text': error_text,
        'for_help_text': for_help_text,
        'payment_support_email': payment_support_email,
        'username': request.user.username,
        'nav_hidden': True,
        'is_request_in_themed_site': is_request_in_themed_site()
    }
    return render_to_response('commerce/checkout_receipt.html', context)
Exemplo n.º 16
0
def fun_settings(request):
    """Add ENVIRONMENT name (Brick, Ketch, dev) to template context when in backoffice application."""
    context = {}
    if request.path.startswith('/backoffice/'):
        context['ENVIRONMENT'] = settings.ENVIRONMENT

        if settings.FEATURES['USE_MICROSITES']:
            context['USE_MICROSITE'] = settings.FEATURES['USE_MICROSITES']
            context['MICROSITE_SITENAME'] = microsite.get_value('SITE_NAME')
            context['MICROSITE_PLATFORM'] = microsite.get_value('platform_name')
    return context
Exemplo n.º 17
0
def get_language_from_request(request, check_path=False):
    """
    Analyzes the request to find what language the user wants the system to
    show. Only languages listed in settings.LANGUAGES are taken into account.
    If the user requests a sublanguage where we have a main language, we send
    out the main language.
    If check_path is True, the URL path prefix will be checked for a language
    code, otherwise this is skipped for backwards compatibility.
    """

    if microsite.get_value('FORCE_LANG'):
        return microsite.get_value('FORCE_LANG')

    if check_path:
        # Note: django 1.4 implementation of get_language_from_path is OK to use
        lang_code = translation.get_language_from_path(request.path_info)
        if lang_code is not None:
            return lang_code

    supported_lang_codes = dict(settings.LANGUAGES)

    if hasattr(request, 'session'):
        lang_code = request.session.get(LANGUAGE_SESSION_KEY)
        # Note: django 1.4 implementation of check_for_language is OK to use
        if lang_code in supported_lang_codes and lang_code is not None and translation.check_for_language(lang_code):
            return lang_code

    lang_code = request.COOKIES.get(settings.LANGUAGE_COOKIE_NAME)

    try:
        return get_supported_language_variant(lang_code)
    except LookupError:
        pass

    accept = request.META.get('HTTP_ACCEPT_LANGUAGE', '')
    # broken in 1.4, so defined above
    for accept_lang, unused in parse_accept_lang_header(accept):
        if accept_lang == '*':
            break

        if not language_code_re.search(accept_lang):
            continue

        try:
            return get_supported_language_variant(accept_lang)
        except LookupError:
            continue

    try:
        return get_supported_language_variant(settings.LANGUAGE_CODE)
    except LookupError:
        return settings.LANGUAGE_CODE
Exemplo n.º 18
0
def is_marketo_course(course_id):
    if not microsite.get_value("course_enable_marketo_integration") and not \
            getattr(settings.FEATURES, "COURSE_ENABLE_MARKETO_INTEGRATION", None):
        return False

    course_map = microsite.get_value("marketo_course_access_field_map", None)
    if not course_map:
            logger.warn("Could not find Marketo course access field map.")
            return False

    if course_id in course_map.keys():
        return True
    else:
        return False
Exemplo n.º 19
0
def get_visible_courses(org=None, filter_=None):
    """
    Return the set of CourseOverviews that should be visible in this branded
    instance.

    Arguments:
        org (string): Optional parameter that allows case-insensitive
            filtering by organization.
        filter_ (dict): Optional parameter that allows custom filtering by
            fields on the course.
    """
    microsite_org = microsite.get_value('course_org_filter')

    if org and microsite_org:
        # When called in the context of a microsite, return an empty result if the org
        # passed by the caller does not match the designated microsite org.
        courses = CourseOverview.get_all_courses(
            org=org,
            filter_=filter_,
        ) if org == microsite_org else []
    else:
        # We only make it to this point if one of org or microsite_org is defined.
        # If both org and microsite_org were defined, the code would have fallen into the
        # first branch of the conditional above, wherein an equality check is performed.
        target_org = org or microsite_org
        courses = CourseOverview.get_all_courses(org=target_org, filter_=filter_)

    courses = sorted(courses, key=lambda course: course.number)

    # When called in the context of a microsite, filtering can stop here.
    if microsite_org:
        return courses

    # See if we have filtered course listings in this domain
    filtered_visible_ids = None

    # this is legacy format which is outside of the microsite feature -- also handle dev case, which should not filter
    subdomain = microsite.get_value('subdomain', 'default')
    if hasattr(settings, 'COURSE_LISTINGS') and subdomain in settings.COURSE_LISTINGS and not settings.DEBUG:
        filtered_visible_ids = frozenset(
            [SlashSeparatedCourseKey.from_deprecated_string(c) for c in settings.COURSE_LISTINGS[subdomain]]
        )

    if filtered_visible_ids:
        return [course for course in courses if course.id in filtered_visible_ids]
    else:
        # Filter out any courses belonging to a microsite, to avoid leaking these.
        microsite_orgs = microsite.get_all_orgs()
        return [course for course in courses if course.location.org not in microsite_orgs]
Exemplo n.º 20
0
def login_page(request):
    """
    Display the login form.
    """
    csrf_token = csrf(request)['csrf_token']
    if (settings.FEATURES['AUTH_USE_CERTIFICATES'] and
            ssl_get_cert_from_request(request)):
        # SSL login doesn't require a login view, so redirect
        # to course now that the user is authenticated via
        # the decorator.
        next_url = request.GET.get('next')
        if next_url:
            return redirect(next_url)
        else:
            return redirect('/course/')
    if settings.FEATURES.get('AUTH_USE_CAS'):
        # If CAS is enabled, redirect auth handling to there
        return redirect(reverse('cas-login'))

    return render_to_response(
        'login.html',
        {
            'csrf': csrf_token,
            'forgot_password_link': "//{base}/login#forgot-password-modal".format(base=settings.LMS_BASE),
            'platform_name': microsite.get_value('platform_name', settings.PLATFORM_NAME),
        }
    )
Exemplo n.º 21
0
def get_or_create_root():
    """
    Returns the root article, or creates it if it doesn't exist.
    """
    try:
        root = URLPath.root()
        if not root.article:
            root.delete()
            raise NoRootURL
        return root
    except NoRootURL:
        pass

    platform_name = microsite.get_value('platform_name', settings.PLATFORM_NAME)
    starting_content = "\n".join((
        _("Welcome to the {platform_name} Wiki").format(platform_name=platform_name),
        "===",
        _("Visit a course wiki to add an article."),
    ))

    root = URLPath.create_root(title=_("Wiki"), content=starting_content)
    article = root.article
    article.group = None
    article.group_read = True
    article.group_write = False
    article.other_read = True
    article.other_write = False
    article.save()

    return root
Exemplo n.º 22
0
def index(request):
    '''
    Redirects to main page -- info page if user authenticated, or marketing if not
    '''

    if settings.COURSEWARE_ENABLED and request.user.is_authenticated():
        return redirect(reverse('dashboard'))

    if settings.FEATURES.get('AUTH_USE_CERTIFICATES'):
        from external_auth.views import ssl_login
        return ssl_login(request)

    enable_mktg_site = microsite.get_value(
        'ENABLE_MKTG_SITE',
        settings.FEATURES.get('ENABLE_MKTG_SITE', False)
    )

    if enable_mktg_site:
        return redirect(settings.MKTG_URLS.get('ROOT'))

    domain = request.META.get('HTTP_HOST')

    # keep specialized logic for Edge until we can migrate over Edge to fully use
    # microsite definitions
    if domain and 'edge.edx.org' in domain:
        context = {
            'suppress_toplevel_navigation': True
        }
        return render_to_response('university_profile/edge.html', context)

    #  we do not expect this case to be reached in cases where
    #  marketing and edge are enabled
    return student.views.index(request, user=request.user)
Exemplo n.º 23
0
def login_page(request):
    """
    Display the login form.
    """
    csrf_token = csrf(request)["csrf_token"]
    if settings.FEATURES["AUTH_USE_CERTIFICATES"] and ssl_get_cert_from_request(request):
        # SSL login doesn't require a login view, so redirect
        # to course now that the user is authenticated via
        # the decorator.
        next_url = request.GET.get("next")
        if next_url:
            return redirect(next_url)
        else:
            return redirect("/course/")
    if settings.FEATURES.get("AUTH_USE_CAS"):
        # If CAS is enabled, redirect auth handling to there
        return redirect(reverse("cas-login"))

    return render_to_response(
        "login.html",
        {
            "csrf": csrf_token,
            "forgot_password_link": "//{base}/login#forgot-password-modal".format(base=settings.LMS_BASE),
            "platform_name": microsite.get_value("platform_name", settings.PLATFORM_NAME),
        },
    )
Exemplo n.º 24
0
def marketing_link(name):
    """Returns the correct URL for a link to the marketing site
    depending on if the marketing site is enabled

    Since the marketing site is enabled by a setting, we have two
    possible URLs for certain links. This function is to decides
    which URL should be provided.
    """

    # link_map maps URLs from the marketing site to the old equivalent on
    # the Django site
    link_map = settings.MKTG_URL_LINK_MAP
    enable_mktg_site = microsite.get_value(
        'ENABLE_MKTG_SITE',
        settings.FEATURES.get('ENABLE_MKTG_SITE', False)
    )

    if enable_mktg_site and name in settings.MKTG_URLS:
        # special case for when we only want the root marketing URL
        if name == 'ROOT':
            return settings.MKTG_URLS.get('ROOT')
        return settings.MKTG_URLS.get('ROOT') + settings.MKTG_URLS.get(name)
    # only link to the old pages when the marketing site isn't on
    elif not enable_mktg_site and name in link_map:
        # don't try to reverse disabled marketing links
        if link_map[name] is not None:
            return reverse(link_map[name])
    else:
        log.warning("Cannot find corresponding link for name: {name}".format(name=name))
        return '#'
Exemplo n.º 25
0
def _footer_mobile_links(is_secure):
    """Return the mobile app store links.

    Args:
        is_secure (bool): Whether the request is using TLS.

    Returns: list

    """
    platform_name = microsite.get_value('platform_name', settings.PLATFORM_NAME)

    mobile_links = []
    if settings.FEATURES.get('ENABLE_FOOTER_MOBILE_APP_LINKS'):
        mobile_links = [
            {
                "name": "apple",
                "title": _(
                    "Download the {platform_name} mobile app from the Apple App Store"
                ).format(platform_name=platform_name),
                "url": settings.MOBILE_STORE_URLS.get('apple', '#'),
                "image": _absolute_url_staticfile(is_secure, 'images/app/app_store_badge_135x40.svg'),
            },
            {
                "name": "google",
                "title": _(
                    "Download the {platform_name} mobile app from Google Play"
                ).format(platform_name=platform_name),
                "url": settings.MOBILE_STORE_URLS.get('google', '#'),
                "image": _absolute_url_staticfile(is_secure, 'images/app/google_play_badge_45.png'),
            }
        ]
    return mobile_links
Exemplo n.º 26
0
def get_processor_exception_html(exception):
    """Return error HTML associated with exception"""

    payment_support_email = microsite.get_value('payment_support_email', settings.PAYMENT_SUPPORT_EMAIL)
    if isinstance(exception, CCProcessorDataException):
        msg = dedent(_(
                """
                <p class="error_msg">
                Sorry! Our payment processor sent us back a payment confirmation that had inconsistent data!
                We apologize that we cannot verify whether the charge went through and take further action on your order.
                The specific error message is: <span class="exception_msg">{msg}</span>.
                Your credit card may possibly have been charged.  Contact us with payment-specific questions at {email}.
                </p>
                """.format(msg=exception.message, email=payment_support_email)))
        return msg
    elif isinstance(exception, CCProcessorWrongAmountException):
        msg = dedent(_(
                """
                <p class="error_msg">
                Sorry! Due to an error your purchase was charged for a different amount than the order total!
                The specific error message is: <span class="exception_msg">{msg}</span>.
                Your credit card has probably been charged. Contact us with payment-specific questions at {email}.
                </p>
                """.format(msg=exception.message, email=payment_support_email)))
        return msg

    # fallthrough case, which basically never happens
    return '<p class="error_msg">EXCEPTION!</p>'
Exemplo n.º 27
0
def show_cart(request):
    """
    This view shows cart items.
    """
    cart = Order.get_cart_for_user(request.user)
    total_cost = cart.total_cost
    cart_items = cart.orderitem_set.all().select_subclasses()
    shoppingcart_items = []
    for cart_item in cart_items:
        course_key = getattr(cart_item, 'course_id')
        if course_key:
            course = get_course_by_id(course_key, depth=0)
            shoppingcart_items.append((cart_item, course))

    site_name = microsite.get_value('SITE_NAME', settings.SITE_NAME)

    callback_url = request.build_absolute_uri(
        reverse("shoppingcart.views.postpay_callback")
    )
    form_html = render_purchase_form_html(cart, callback_url=callback_url)
    context = {
        'order': cart,
        'shoppingcart_items': shoppingcart_items,
        'amount': total_cost,
        'site_name': site_name,
        'form_html': form_html,
    }
    return render_to_response("shoppingcart/shopping_cart.html", context)
Exemplo n.º 28
0
def view_student_survey(user, survey_name, course=None, redirect_url=None, is_required=False, skip_redirect_url=None):
    """
    Shared utility method to render a survey form
    NOTE: This method is shared between the Survey and Courseware Djangoapps
    """

    redirect_url = redirect_url if redirect_url else reverse("dashboard")
    dashboard_redirect_url = reverse("dashboard")
    skip_redirect_url = skip_redirect_url if skip_redirect_url else dashboard_redirect_url

    survey = SurveyForm.get(survey_name, throw_if_not_found=False)
    if not survey:
        return HttpResponseRedirect(redirect_url)

    # the result set from get_answers, has an outer key with the user_id
    # just remove that outer key to make the JSON payload simplier
    existing_answers = survey.get_answers(user=user).get(user.id, {})

    context = {
        "existing_data_json": json.dumps(existing_answers),
        "postback_url": reverse("submit_answers", args=[survey_name]),
        "redirect_url": redirect_url,
        "skip_redirect_url": skip_redirect_url,
        "dashboard_redirect_url": dashboard_redirect_url,
        "survey_form": survey.form,
        "is_required": is_required,
        "mail_to_link": microsite.get_value("email_from_address", settings.CONTACT_EMAIL),
        "course": course,
    }

    return render_to_response("survey/survey.html", context)
Exemplo n.º 29
0
def _get_processor_decline_html(params):
    """
    Return HTML indicating that the user's payment was declined.

    Args:
        params (dict): Parameters we received from CyberSource.

    Returns:
        unicode: The rendered HTML.

    """
    payment_support_email = microsite.get_value("payment_support_email", settings.PAYMENT_SUPPORT_EMAIL)
    return _format_error_html(
        _(
            "Sorry! Our payment processor did not accept your payment.  "
            "The decision they returned was {decision}, "
            "and the reason was {reason}.  "
            "You were not charged. Please try a different form of payment.  "
            "Contact us with payment-related questions at {email}."
        ).format(
            decision='<span class="decision">{decision}</span>'.format(decision=params["decision"]),
            reason='<span class="reason">{reason_code}:{reason_msg}</span>'.format(
                reason_code=params["reason_code"], reason_msg=REASONCODE_MAP.get(params["reason_code"])
            ),
            email=payment_support_email,
        )
    )
Exemplo n.º 30
0
def checkout_receipt(request):
    """ Receipt view. """
    context = {
        'platform_name': microsite.get_value('platform_name', settings.PLATFORM_NAME),
        'verified': SoftwareSecurePhotoVerification.verification_valid_or_pending(request.user).exists()
    }
    return render_to_response('commerce/checkout_receipt.html', context)
Exemplo n.º 31
0
def user_detail(request, username):
    if settings.FEATURES['USE_MICROSITES']:
        users = User.objects.filter(usersignupsource__site=microsite.get_value('SITE_NAME'))
    else:
        users = User.objects.all()
    try:
        user = users.select_related('profile').get(username=username)
    except User.DoesNotExist:
        raise Http404()

    if 'action' in request.POST:
        action = request.POST.get('action')
        if action in user_actions:
            user_actions[action](request, user)
        else:
            messages.error(request, _(u"Invalid user action."))
        return redirect('backoffice:user-detail', username=username)

    certificates = [{'cert': cert, 'hashid': hashid_for_verified(cert)}
            for cert in GeneratedCertificate.objects.filter(user=user)]
    userform = UserForm(instance=user, data=request.POST or None)
    userprofileform = UserProfileForm(instance=user.profile if hasattr(user, 'profile') else None,
            data=request.POST or None)

    disabled = UserStanding.objects.filter(user=user,
                                           account_status=UserStanding.ACCOUNT_DISABLED)

    enrollments = []
    optouts = Optout.objects.filter(user=user).values_list('course_id', flat=True)
    user_roles = defaultdict(list)
    for car in CourseAccessRole.objects.filter(user=user).exclude(course_id=CourseKeyField.Empty):
        user_roles[unicode(car.course_id)].append(car.role)

    course_modes = defaultdict(list)
    modes = CourseMode.objects.all()
    for course in modes:
        course_modes[unicode(course.course_id)].append([course.mode_slug, course.min_price])

    for enrollment in CourseEnrollment.objects.filter(user=user):
        key = unicode(enrollment.course_id)
        optout = key in optouts
        course = get_course(key)
        if not course:
            continue  # enrollment can exists for course that does not exist anymore in mongo
        title = course.display_name
        course_roles = user_roles.get(key, [])
        enrollments.append((title, unicode(enrollment.course_id), optout, enrollment.mode,
                course_roles, enrollment.is_active))

    if request.method == 'POST':
        if all([userform.is_valid(), userprofileform.is_valid()]):
            userform.save()
            userprofileform.save()
            messages.success(request, _(u"User %s has been modified") % user.username)
            return redirect('backoffice:user-list')

    return render(request, 'backoffice/user.html', {
        'userform': userform,
        'userprofileform': userprofileform,
        'enrollments': enrollments,
        'disabled': disabled,
        'tab': 'users',
        'certificates': certificates,
        'course_modes': course_modes,
        'payment_terms': get_accepted_payment_terms(user)})
Exemplo n.º 32
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

    # 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
        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 request.GET.get('preview', None):
            user_certificate = GeneratedCertificate(
                mode=request.GET.get('preview'),
                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, request.GET.get('preview'))
    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)

    # 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()
    if linkedin_config.enabled:
        context['linked_in_url'] = linkedin_config.add_to_profile_url(
            course.id, course.display_name, user_certificate.mode,
            request.build_absolute_uri(
                get_certificate_url(user_id=user.id,
                                    course_id=unicode(course.id))))

    # 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)
            context = RequestContext(request, context)
            return HttpResponse(template.render(context))

    return render_to_response("certificates/valid.html", context)
Exemplo n.º 33
0
def _update_certificate_context(context, course, user, user_certificate):
    """
    Build up the certificate web view context using the provided values
    (Helper method to keep the view clean)
    """
    # Populate dynamic output values using the course/certificate data loaded above
    user_fullname = user.profile.name
    platform_name = microsite.get_value("platform_name",
                                        settings.PLATFORM_NAME)
    certificate_type = context.get('certificate_type')
    partner_short_name = course.org
    partner_long_name = None
    organizations = organization_api.get_course_organizations(
        course_id=course.id)
    if organizations:
        #TODO Need to add support for multiple organizations, Currently we are interested in the first one.
        organization = organizations[0]
        partner_long_name = organization.get('name', partner_long_name)
        partner_short_name = organization.get('short_name', partner_short_name)
        context['organization_long_name'] = partner_long_name
        context['organization_short_name'] = partner_short_name
        context['organization_logo'] = organization.get('logo', None)

    context['username'] = user.username
    context['course_mode'] = user_certificate.mode
    context['accomplishment_user_id'] = user.id
    context['accomplishment_copy_name'] = user_fullname
    context['accomplishment_copy_username'] = user.username
    context['accomplishment_copy_course_org'] = partner_short_name
    context['accomplishment_copy_course_name'] = course.display_name
    context['course_image_url'] = course_image_url(course)
    context['share_settings'] = settings.FEATURES.get(
        'SOCIAL_SHARING_SETTINGS', {})
    context['course_number'] = course.number
    try:
        badge = BadgeAssertion.objects.get(
            user=user, course_id=course.location.course_key)
    except BadgeAssertion.DoesNotExist:
        badge = None
    context['badge'] = badge

    # Override the defaults with any mode-specific static values
    context['certificate_id_number'] = user_certificate.verify_uuid
    context['certificate_verify_url'] = "{prefix}{uuid}{suffix}".format(
        prefix=context.get('certificate_verify_url_prefix'),
        uuid=user_certificate.verify_uuid,
        suffix=context.get('certificate_verify_url_suffix'))

    # Translators:  The format of the date includes the full name of the month
    context['certificate_date_issued'] = _('{month} {day}, {year}').format(
        month=user_certificate.modified_date.strftime("%B"),
        day=user_certificate.modified_date.day,
        year=user_certificate.modified_date.year)

    if partner_long_name:
        context['accomplishment_copy_course_description'] = _(
            'a course of study offered by {partner_short_name}, an '
            'online learning initiative of {partner_long_name} '
            'through {platform_name}.').format(
                partner_short_name=partner_short_name,
                partner_long_name=partner_long_name,
                platform_name=platform_name)
    else:
        context['accomplishment_copy_course_description'] = _(
            'a course of study offered by {partner_short_name}, '
            'through {platform_name}.').format(
                partner_short_name=partner_short_name,
                platform_name=platform_name)

    # Translators: Accomplishments describe the awards/certifications obtained by students on this platform
    context['accomplishment_copy_about'] = _(
        'About {platform_name} Accomplishments').format(
            platform_name=platform_name)

    context['accomplishment_more_title'] = _(
        "More Information About {user_name}'s Certificate:").format(
            user_name=user_fullname)

    # Translators:  This line appears on the page just before the generation date for the certificate
    context['certificate_date_issued_title'] = _("Issued On:")

    # Translators:  The Certificate ID Number is an alphanumeric value unique to each individual certificate
    context['certificate_id_number_title'] = _('Certificate ID Number')

    context['certificate_info_title'] = _('About {platform_name} Certificates'
                                          ).format(platform_name=platform_name)

    # Translators: This text describes the purpose (and therefore, value) of a course certificate
    # 'verifying your identity' refers to the process for establishing the authenticity of the student
    context['certificate_info_description'] = _(
        "{platform_name} acknowledges achievements through certificates, which "
        "are awarded for various activities {platform_name} students complete "
        "under the <a href='{tos_url}'>{platform_name} Honor Code</a>.  Some "
        "certificates require completing additional steps, such as "
        "<a href='{verified_cert_url}'> verifying your identity</a>.").format(
            platform_name=platform_name,
            tos_url=context.get('company_tos_url'),
            verified_cert_url=context.get('company_verified_certificate_url'))

    context['certificate_verify_title'] = _(
        "How {platform_name} Validates Student Certificates").format(
            platform_name=platform_name)

    # Translators:  This text describes the validation mechanism for a certificate file (known as GPG security)
    context['certificate_verify_description'] = _(
        'Certificates issued by {platform_name} are signed by a gpg key so '
        'that they can be validated independently by anyone with the '
        '{platform_name} public key. For independent verification, '
        '{platform_name} uses what is called a '
        '"detached signature"&quot;".').format(platform_name=platform_name)

    context['certificate_verify_urltext'] = _(
        "Validate this certificate for yourself")

    # Translators:  This text describes (at a high level) the mission and charter the edX platform and organization
    context['company_about_description'] = _(
        "{platform_name} offers interactive online classes and MOOCs from the "
        "world's best universities, including MIT, Harvard, Berkeley, University "
        "of Texas, and many others.  {platform_name} is a non-profit online "
        "initiative created by founding partners Harvard and MIT.").format(
            platform_name=platform_name)

    context['company_about_title'] = _("About {platform_name}").format(
        platform_name=platform_name)

    context['company_about_urltext'] = _(
        "Learn more about {platform_name}").format(platform_name=platform_name)

    context['company_courselist_urltext'] = _(
        "Learn with {platform_name}").format(platform_name=platform_name)

    context['company_careers_urltext'] = _("Work at {platform_name}").format(
        platform_name=platform_name)

    context['company_contact_urltext'] = _("Contact {platform_name}").format(
        platform_name=platform_name)

    # Translators:  This text appears near the top of the certficate and describes the guarantee provided by edX
    context['document_banner'] = _(
        "{platform_name} acknowledges the following student accomplishment"
    ).format(platform_name=platform_name)

    # Translators:  This text represents the verification of the certificate
    context['document_meta_description'] = _(
        'This is a valid {platform_name} certificate for {user_name}, '
        'who participated in {partner_short_name} {course_number}').format(
            platform_name=platform_name,
            user_name=user_fullname,
            partner_short_name=partner_short_name,
            course_number=course.number)

    # Translators:  This text is bound to the HTML 'title' element of the page and appears in the browser title bar
    context['document_title'] = _(
        "{partner_short_name} {course_number} Certificate | {platform_name}"
    ).format(partner_short_name=partner_short_name,
             course_number=course.number,
             platform_name=platform_name)

    # Translators:  This text fragment appears after the student's name (displayed in a large font) on the certificate
    # screen.  The text describes the accomplishment represented by the certificate information displayed to the user
    context['accomplishment_copy_description_full'] = _(
        "successfully completed, received a passing grade, and was "
        "awarded a {platform_name} {certificate_type} "
        "Certificate of Completion in ").format(
            platform_name=platform_name,
            certificate_type=context.get("certificate_type"))

    certificate_type_description = get_certificate_description(
        user_certificate.mode, certificate_type, platform_name)
    if certificate_type_description:
        context['certificate_type_description'] = certificate_type_description

    # Translators: This line is displayed to a user who has completed a course and achieved a certification
    context['accomplishment_banner_opening'] = _(
        "{fullname}, you've earned a certificate!").format(
            fullname=user_fullname)

    # Translators: This line congratulates the user and instructs them to share their accomplishment on social networks
    context['accomplishment_banner_congrats'] = _(
        "Congratulations! This page summarizes all of the details of what "
        "you've accomplished. Show it off to family, friends, and colleagues "
        "in your social and professional networks.")

    # Translators: This line leads the reader to understand more about the certificate that a student has been awarded
    context['accomplishment_copy_more_about'] = _(
        "More about {fullname}'s accomplishment").format(
            fullname=user_fullname)
Exemplo n.º 34
0
def course_about(request, course_id):
    """
    Display the course's about page.

    Assumes the course_id is in a valid format.
    """

    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)

    permission_name = microsite.get_value(
        'COURSE_ABOUT_VISIBILITY_PERMISSION',
        settings.COURSE_ABOUT_VISIBILITY_PERMISSION)
    course = get_course_with_access(request.user, permission_name, course_key)

    if microsite.get_value('ENABLE_MKTG_SITE',
                           settings.FEATURES.get('ENABLE_MKTG_SITE', False)):
        return redirect(
            reverse('info', args=[course.id.to_deprecated_string()]))

    registered = registered_for_course(course, request.user)

    staff_access = has_access(request.user, 'staff', course)
    studio_url = get_studio_url(course, 'settings/details')

    if has_access(request.user, 'load', course):
        course_target = reverse('info',
                                args=[course.id.to_deprecated_string()])
    else:
        course_target = reverse('about_course',
                                args=[course.id.to_deprecated_string()])

    show_courseware_link = (has_access(request.user, 'load', course)
                            or settings.FEATURES.get('ENABLE_LMS_MIGRATION'))

    # Note: this is a flow for payment for course registration, not the Verified Certificate flow.
    registration_price = 0
    in_cart = False
    reg_then_add_to_cart_link = ""

    _is_shopping_cart_enabled = is_shopping_cart_enabled()
    if (_is_shopping_cart_enabled):
        registration_price = CourseMode.min_course_price_for_currency(
            course_key, settings.PAID_COURSE_REGISTRATION_CURRENCY[0])
        if request.user.is_authenticated():
            cart = shoppingcart.models.Order.get_cart_for_user(request.user)
            in_cart = shoppingcart.models.PaidCourseRegistration.contained_in_order(cart, course_key) or \
                shoppingcart.models.CourseRegCodeItem.contained_in_order(cart, course_key)

        reg_then_add_to_cart_link = "{reg_url}?course_id={course_id}&enrollment_action=add_to_cart".format(
            reg_url=reverse('register_user'),
            course_id=course.id.to_deprecated_string())

    # Used to provide context to message to student if enrollment not allowed
    can_enroll = has_access(request.user, 'enroll', course)
    invitation_only = course.invitation_only
    is_course_full = CourseEnrollment.is_course_full(course)

    # Register button should be disabled if one of the following is true:
    # - Student is already registered for course
    # - Course is already full
    # - Student cannot enroll in course
    active_reg_button = not (registered or is_course_full or not can_enroll)

    is_shib_course = uses_shib(course)

    return render_to_response(
        'courseware/course_about.html',
        {
            'course': course,
            'staff_access': staff_access,
            'studio_url': studio_url,
            'registered': registered,
            'course_target': course_target,
            'registration_price': registration_price,
            'in_cart': in_cart,
            'reg_then_add_to_cart_link': reg_then_add_to_cart_link,
            'show_courseware_link': show_courseware_link,
            'is_course_full': is_course_full,
            'can_enroll': can_enroll,
            'invitation_only': invitation_only,
            'active_reg_button': active_reg_button,
            'is_shib_course': is_shib_course,
            # We do not want to display the internal courseware header, which is used when the course is found in the
            # context. This value is therefor explicitly set to render the appropriate header.
            'disable_courseware_header': True,
            'is_shopping_cart_enabled': _is_shopping_cart_enabled,
            'cart_link': reverse('shoppingcart.views.show_cart'),
        })
Exemplo n.º 35
0
def _do_unenroll_students(course_key, students, email_students=False):
    """
    Do the actual work of un-enrolling multiple students, presented as a string
    of emails separated by commas or returns
    `course_key` is id of course (a `str`)
    `students` is string of student emails separated by commas or returns (a `str`)
    `email_students` is user input preference (a `boolean`)
    """

    old_students, __ = get_and_clean_student_list(students)
    status = dict([x, 'unprocessed'] for x in old_students)

    stripped_site_name = microsite.get_value(
        'SITE_NAME',
        settings.SITE_NAME
    )
    if email_students:
        course = modulestore().get_course(course_key)
        # Composition of email
        data = {
            'site_name': stripped_site_name,
            'course': course
        }

    for student in old_students:

        isok = False
        cea = CourseEnrollmentAllowed.objects.filter(course_id=course_key, email=student)
        # Will be 0 or 1 records as there is a unique key on email + course_id
        if cea:
            cea[0].delete()
            status[student] = "un-enrolled"
            isok = True

        try:
            user = User.objects.get(email=student)
        except User.DoesNotExist:

            if isok and email_students:
                # User was allowed to join but had not signed up yet
                data['email_address'] = student
                data['message'] = 'allowed_unenroll'
                send_mail_ret = send_mail_to_student(student, data)
                status[student] += (', email sent' if send_mail_ret else '')

            continue

        # Will be 0 or 1 records as there is a unique key on user + course_id
        if CourseEnrollment.is_enrolled(user, course_key):
            try:
                CourseEnrollment.unenroll(user, course_key)
                status[student] = "un-enrolled"
                if email_students:
                    # User was enrolled
                    data['email_address'] = student
                    data['full_name'] = user.profile.name
                    data['message'] = 'enrolled_unenroll'
                    send_mail_ret = send_mail_to_student(student, data)
                    status[student] += (', email sent' if send_mail_ret else '')

            except Exception:  # pylint: disable=broad-except
                if not isok:
                    status[student] = "Error!  Failed to un-enroll"

    datatable = {'header': ['StudentEmail', 'action']}
    datatable['data'] = [[x, status[x]] for x in sorted(status)]
    datatable['title'] = _('Un-enrollment of students')

    return dict(datatable=datatable)
Exemplo n.º 36
0
def show_receipt(request, ordernum):
    """
    Displays a receipt for a particular order.
    404 if order is not yet purchased or request.user != order.user
    """

    try:
        order = Order.objects.get(id=ordernum)
    except Order.DoesNotExist:
        raise Http404('Order not found!')

    if order.user != request.user or order.status != 'purchased':
        raise Http404('Order not found!')

    order_items = OrderItem.objects.filter(order=order).select_subclasses()
    shoppingcart_items = []
    course_names_list = []
    for order_item in order_items:
        course_key = getattr(order_item, 'course_id')
        if course_key:
            course = get_course_by_id(course_key, depth=0)
            shoppingcart_items.append((order_item, course))
            course_names_list.append(course.display_name)

    appended_course_names = ", ".join(course_names_list)
    any_refunds = any(i.status == "refunded" for i in order_items)
    receipt_template = 'shoppingcart/receipt.html'
    __, instructions = order.generate_receipt_instructions()
    order_type = getattr(order, 'order_type')

    # Only orders where order_items.count() == 1 might be attempting to upgrade
    attempting_upgrade = request.session.get('attempting_upgrade', False)
    if attempting_upgrade:
        course_enrollment = CourseEnrollment.get_or_create_enrollment(request.user, order_items[0].course_id)
        course_enrollment.emit_event(EVENT_NAME_USER_UPGRADED)
        request.session['attempting_upgrade'] = False

    recipient_list = []
    registration_codes = None
    total_registration_codes = None
    recipient_list.append(getattr(order.user, 'email'))
    if order_type == OrderTypes.BUSINESS:
        registration_codes = CourseRegistrationCode.objects.filter(order=order)
        total_registration_codes = registration_codes.count()
        if order.company_contact_email:
            recipient_list.append(order.company_contact_email)
        if order.recipient_email:
            recipient_list.append(order.recipient_email)

    appended_recipient_emails = ", ".join(recipient_list)

    context = {
        'order': order,
        'shoppingcart_items': shoppingcart_items,
        'any_refunds': any_refunds,
        'instructions': instructions,
        'site_name': microsite.get_value('SITE_NAME', settings.SITE_NAME),
        'order_type': order_type,
        'appended_course_names': appended_course_names,
        'appended_recipient_emails': appended_recipient_emails,
        'total_registration_codes': total_registration_codes,
        'registration_codes': registration_codes,
        'order_purchase_date': order.purchase_time.strftime("%B %d, %Y"),
    }
    # we want to have the ability to override the default receipt page when
    # there is only one item in the order
    if order_items.count() == 1:
        receipt_template = order_items[0].single_item_receipt_template
        context.update(order_items[0].single_item_receipt_context)

    return render_to_response(receipt_template, context)
Exemplo n.º 37
0
def course_about(request, course_id):
    """
    Display the course's about page.

    Assumes the course_id is in a valid format.
    """

    if microsite.get_value('ENABLE_MKTG_SITE',
                           settings.FEATURES.get('ENABLE_MKTG_SITE', False)):
        raise Http404

    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)

    course = get_course_with_access(request.user, 'see_exists', course_key)
    registered = registered_for_course(course, request.user)
    staff_access = has_access(request.user, 'staff', course)
    studio_url = get_studio_url(course, 'settings/details')

    if has_access(request.user, 'load', course):
        course_target = reverse('info',
                                args=[course.id.to_deprecated_string()])
    else:
        course_target = reverse('about_course',
                                args=[course.id.to_deprecated_string()])

    show_courseware_link = (has_access(request.user, 'load', course)
                            or settings.FEATURES.get('ENABLE_LMS_MIGRATION'))

    # Note: this is a flow for payment for course registration, not the Verified Certificate flow.
    registration_price = 0
    in_cart = False
    reg_then_add_to_cart_link = ""
    if (settings.FEATURES.get('ENABLE_SHOPPING_CART')
            and settings.FEATURES.get('ENABLE_PAID_COURSE_REGISTRATION')):
        registration_price = CourseMode.min_course_price_for_currency(
            course_key, settings.PAID_COURSE_REGISTRATION_CURRENCY[0])
        if request.user.is_authenticated():
            cart = shoppingcart.models.Order.get_cart_for_user(request.user)
            in_cart = shoppingcart.models.PaidCourseRegistration.contained_in_order(
                cart, course_key)

        reg_then_add_to_cart_link = "{reg_url}?course_id={course_id}&enrollment_action=add_to_cart".format(
            reg_url=reverse('register_user'),
            course_id=course.id.to_deprecated_string())

    # Used to provide context to message to student if enrollment not allowed
    can_enroll = has_access(request.user, 'enroll', course)
    invitation_only = course.invitation_only
    is_course_full = CourseEnrollment.is_course_full(course)

    # Register button should be disabled if one of the following is true:
    # - Student is already registered for course
    # - Course is already full
    # - Student cannot enroll in course
    active_reg_button = not (registered or is_course_full or not can_enroll)

    is_shib_course = uses_shib(course)

    return render_to_response(
        'courseware/course_about.html', {
            'course': course,
            'staff_access': staff_access,
            'studio_url': studio_url,
            'registered': registered,
            'course_target': course_target,
            'registration_price': registration_price,
            'in_cart': in_cart,
            'reg_then_add_to_cart_link': reg_then_add_to_cart_link,
            'show_courseware_link': show_courseware_link,
            'is_course_full': is_course_full,
            'can_enroll': can_enroll,
            'invitation_only': invitation_only,
            'active_reg_button': active_reg_button,
            'is_shib_course': is_shib_course,
        })
Exemplo n.º 38
0
def email_change_request_handler(request):
    """Handle a request to change the user's email address.

    Sends an email to the newly specified address containing a link
    to a confirmation page.

    Args:
        request (HttpRequest)

    Returns:
        HttpResponse: 200 if the confirmation email was sent successfully
        HttpResponse: 302 if not logged in (redirect to login page)
        HttpResponse: 400 if the format of the new email is incorrect, or if
            an email change is requested for a user which does not exist
        HttpResponse: 401 if the provided password (in the form) is incorrect
        HttpResponse: 405 if using an unsupported HTTP method
        HttpResponse: 409 if the provided email is already in use

    Example usage:

        POST /account/email

    """
    username = request.user.username
    password = request.POST.get('password')
    new_email = request.POST.get('email')

    if new_email is None:
        return HttpResponseBadRequest("Missing param 'email'")
    if password is None:
        return HttpResponseBadRequest("Missing param 'password'")

    old_email = profile_api.profile_info(username)['email']

    try:
        key = account_api.request_email_change(username, new_email, password)
    except (account_api.AccountEmailInvalid, account_api.AccountUserNotFound):
        return HttpResponseBadRequest()
    except account_api.AccountEmailAlreadyExists:
        return HttpResponse(status=409)
    except account_api.AccountNotAuthorized:
        return HttpResponse(status=401)

    context = {
        'key': key,
        'old_email': old_email,
        'new_email': new_email,
    }

    subject = render_to_string('student_account/emails/email_change_request/subject_line.txt', context)
    subject = ''.join(subject.splitlines())
    message = render_to_string('student_account/emails/email_change_request/message_body.txt', context)

    from_address = microsite.get_value(
        'email_from_address',
        settings.DEFAULT_FROM_EMAIL
    )

    # Send a confirmation email to the new address containing the activation key
    send_mail(subject, message, from_address, [new_email])

    return HttpResponse(status=200)
Exemplo n.º 39
0
def get_university_for_request():
    """
    Return the university name specified for the domain, or None
    if no university was specified
    """
    return microsite.get_value('university')
Exemplo n.º 40
0
def email_change_confirmation_handler(request, key):
    """Complete a change of the user's email address.

    This is called when the activation link included in the confirmation
    email is clicked.

    Args:
        request (HttpRequest)

    Returns:
        HttpResponse: 200 if the email change is successful, the activation key
            is invalid, the new email is already in use, or the
            user to which the email change will be applied does
            not exist
        HttpResponse: 302 if not logged in (redirect to login page)
        HttpResponse: 405 if using an unsupported HTTP method

    Example usage:

        GET /account/email/confirmation/{key}

    """
    try:
        old_email, new_email = account_api.confirm_email_change(key)
    except account_api.AccountNotAuthorized:
        return render_to_response(
            'student_account/email_change_failed.html', {
                'disable_courseware_js': True,
                'error': 'key_invalid',
            }
        )
    except account_api.AccountEmailAlreadyExists:
        return render_to_response(
            'student_account/email_change_failed.html', {
                'disable_courseware_js': True,
                'error': 'email_used',
            }
        )
    except account_api.AccountInternalError:
        return render_to_response(
            'student_account/email_change_failed.html', {
                'disable_courseware_js': True,
                'error': 'internal',
            }
        )

    context = {
        'old_email': old_email,
        'new_email': new_email,
    }

    subject = render_to_string('student_account/emails/email_change_confirmation/subject_line.txt', context)
    subject = ''.join(subject.splitlines())
    message = render_to_string('student_account/emails/email_change_confirmation/message_body.txt', context)

    from_address = microsite.get_value(
        'email_from_address',
        settings.DEFAULT_FROM_EMAIL
    )

    # Notify both old and new emails of the change
    send_mail(subject, message, from_address, [old_email, new_email])

    return render_to_response(
        'student_account/email_change_successful.html', {
            'disable_courseware_js': True,
        }
    )
Exemplo n.º 41
0
    url(r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict), )

# sysadmin dashboard, to see what courses are loaded, to delete & load courses
if settings.FEATURES["ENABLE_SYSADMIN_DASHBOARD"]:
    urlpatterns += (url(r'^sysadmin/', include('dashboard.sysadmin_urls')), )

urlpatterns += (url(r'^support/', include('dashboard.support_urls')), )

#Semi-static views (these need to be rendered and have the login bar, but don't change)
urlpatterns += (url(r'^404$',
                    'static_template_view.views.render',
                    {'template': '404.html'},
                    name="404"), )

# Favicon
favicon_path = microsite.get_value('favicon_path', settings.FAVICON_PATH)
urlpatterns += ((r'^favicon\.ico$', 'django.views.generic.simple.redirect_to',
                 {
                     'url': settings.STATIC_URL + favicon_path
                 }), )

# Semi-static views only used by edX, not by themes
if not settings.FEATURES["USE_CUSTOM_THEME"]:
    urlpatterns += (
        url(r'^blog$',
            'static_template_view.views.render', {'template': 'blog.html'},
            name="blog"),
        url(r'^contact$',
            'static_template_view.views.render', {'template': 'contact.html'},
            name="contact"),
        url(r'^donate$',
Exemplo n.º 42
0
def microsite_get_value(value, default=None):
    """
    Django template filter that wrapps the microsite.get_value function
    """
    return microsite.get_value(value, default)
Exemplo n.º 43
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 = {
        '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,
    }

    return render_to_response('student_account/login_and_register.html',
                              context)
Exemplo n.º 44
0
    def purchase(self, first='', last='', street1='', street2='', city='', state='', postalcode='',
                 country='', ccnum='', cardtype='', processor_reply_dump=''):
        """
        Call to mark this order as purchased.  Iterates through its OrderItems and calls
        their purchased_callback

        `first` - first name of person billed (e.g. John)
        `last` - last name of person billed (e.g. Smith)
        `street1` - first line of a street address of the billing address (e.g. 11 Cambridge Center)
        `street2` - second line of a street address of the billing address (e.g. Suite 101)
        `city` - city of the billing address (e.g. Cambridge)
        `state` - code of the state, province, or territory of the billing address (e.g. MA)
        `postalcode` - postal code of the billing address (e.g. 02142)
        `country` - country code of the billing address (e.g. US)
        `ccnum` - last 4 digits of the credit card number of the credit card billed (e.g. 1111)
        `cardtype` - 3-digit code representing the card type used (e.g. 001)
        `processor_reply_dump` - all the parameters returned by the processor

        """
        if self.status == 'purchased':
            return
        self.status = 'purchased'
        self.purchase_time = datetime.now(pytz.utc)
        self.bill_to_first = first
        self.bill_to_last = last
        self.bill_to_city = city
        self.bill_to_state = state
        self.bill_to_country = country
        self.bill_to_postalcode = postalcode
        if settings.FEATURES['STORE_BILLING_INFO']:
            self.bill_to_street1 = street1
            self.bill_to_street2 = street2
            self.bill_to_ccnum = ccnum
            self.bill_to_cardtype = cardtype
            self.processor_reply_dump = processor_reply_dump

        # save these changes on the order, then we can tell when we are in an
        # inconsistent state
        self.save()
        # this should return all of the objects with the correct types of the
        # subclasses
        orderitems = OrderItem.objects.filter(order=self).select_subclasses()
        for item in orderitems:
            item.purchase_item()

        # send confirmation e-mail
        subject = _("Order Payment Confirmation")
        message = render_to_string(
            'emails/order_confirmation_email.txt',
            {
                'order': self,
                'order_items': orderitems,
                'has_billing_info': settings.FEATURES['STORE_BILLING_INFO']
            }
        )
        try:
            from_address = microsite.get_value(
                'email_from_address',
                settings.DEFAULT_FROM_EMAIL
            )

            send_mail(subject, message,
                      from_address, [self.user.email])  # pylint: disable=E1101
        except (smtplib.SMTPException, BotoServerError):  # sadly need to handle diff. mail backends individually
            log.error('Failed sending confirmation e-mail for order %d', self.id)  # pylint: disable=E1101
Exemplo n.º 45
0
def checkout_cancel(_request):
    """ Checkout/payment cancellation view. """
    context = {'payment_support_email': microsite.get_value('payment_support_email', settings.PAYMENT_SUPPORT_EMAIL)}
    return render_to_response("commerce/checkout_cancel.html", context)
Exemplo n.º 46
0
def _record_feedback_in_zendesk(realname, email, subject, details, tags,
                                additional_info):
    """
    Create a new user-requested Zendesk ticket.

    Once created, the ticket will be updated with a private comment containing
    additional information from the browser and server, such as HTTP headers
    and user state. Returns a boolean value indicating whether ticket creation
    was successful, regardless of whether the private comment update succeeded.
    """
    zendesk_api = _ZendeskApi()

    additional_info_string = ("Additional information:\n\n" + "\n".join(
        "%s: %s" % (key, value)
        for (key, value) in additional_info.items() if value is not None))

    # Tag all issues with LMS to distinguish channel in Zendesk; requested by student support team
    zendesk_tags = list(tags.values()) + ["LMS"]

    # Per edX support, we would like to be able to route white label feedback items
    # via tagging
    white_label_org = microsite.get_value('course_org_filter')
    if white_label_org:
        zendesk_tags = zendesk_tags + [
            "whitelabel_{org}".format(org=white_label_org)
        ]

    new_ticket = {
        "ticket": {
            "requester": {
                "name": realname,
                "email": email
            },
            "subject": subject,
            "comment": {
                "body": details
            },
            "tags": zendesk_tags
        }
    }
    try:
        ticket_id = zendesk_api.create_ticket(new_ticket)
    except zendesk.ZendeskError:
        log.exception("Error creating Zendesk ticket")
        return False

    # Additional information is provided as a private update so the information
    # is not visible to the user.
    ticket_update = {
        "ticket": {
            "comment": {
                "public": False,
                "body": additional_info_string
            }
        }
    }
    try:
        zendesk_api.update_ticket(ticket_id, ticket_update)
    except zendesk.ZendeskError:
        log.exception("Error updating Zendesk ticket")
        # The update is not strictly necessary, so do not indicate failure to the user
        pass

    return True
Exemplo n.º 47
0
def filter_queryset_for_site(queryset):
    if settings.FEATURES['USE_MICROSITES']:
        queryset = queryset.filter(microsite=microsite.get_value('SITE_NAME'))
    return queryset
Exemplo n.º 48
0
def _show_receipt_html(request, order):
    """Render the receipt page as HTML.

    Arguments:
        request (HttpRequest): The request for the receipt.
        order (Order): The order model to display.

    Returns:
        HttpResponse

    """
    order_items = OrderItem.objects.filter(order=order).select_subclasses()
    shoppingcart_items = []
    course_names_list = []
    for order_item in order_items:
        course_key = getattr(order_item, 'course_id')
        if course_key:
            course = get_course_by_id(course_key, depth=0)
            shoppingcart_items.append((order_item, course))
            course_names_list.append(course.display_name)

    appended_course_names = ", ".join(course_names_list)
    any_refunds = any(i.status == "refunded" for i in order_items)
    receipt_template = 'shoppingcart/receipt.html'
    __, instructions = order.generate_receipt_instructions()
    order_type = getattr(order, 'order_type')

    # Only orders where order_items.count() == 1 might be attempting to upgrade
    attempting_upgrade = request.session.get('attempting_upgrade', False)
    if attempting_upgrade:
        course_enrollment = CourseEnrollment.get_or_create_enrollment(request.user, order_items[0].course_id)
        course_enrollment.emit_event(EVENT_NAME_USER_UPGRADED)
        request.session['attempting_upgrade'] = False

    recipient_list = []
    total_registration_codes = None
    reg_code_info_list = []
    recipient_list.append(getattr(order.user, 'email'))
    if order_type == OrderTypes.BUSINESS:
        if order.company_contact_email:
            recipient_list.append(order.company_contact_email)
        if order.recipient_email:
            recipient_list.append(order.recipient_email)

        for __, course in shoppingcart_items:
            course_registration_codes = CourseRegistrationCode.objects.filter(order=order, course_id=course.id)
            total_registration_codes = course_registration_codes.count()
            for course_registration_code in course_registration_codes:
                reg_code_info_list.append({
                    'course_name': course.display_name,
                    'redemption_url': reverse('register_code_redemption', args=[course_registration_code.code]),
                    'code': course_registration_code.code,
                    'is_redeemed': RegistrationCodeRedemption.objects.filter(
                        registration_code=course_registration_code).exists(),
                })

    appended_recipient_emails = ", ".join(recipient_list)

    context = {
        'order': order,
        'shoppingcart_items': shoppingcart_items,
        'any_refunds': any_refunds,
        'instructions': instructions,
        'site_name': microsite.get_value('SITE_NAME', settings.SITE_NAME),
        'order_type': order_type,
        'appended_course_names': appended_course_names,
        'appended_recipient_emails': appended_recipient_emails,
        'currency_symbol': settings.PAID_COURSE_REGISTRATION_CURRENCY[1],
        'currency': settings.PAID_COURSE_REGISTRATION_CURRENCY[0],
        'total_registration_codes': total_registration_codes,
        'reg_code_info_list': reg_code_info_list,
        'order_purchase_date': order.purchase_time.strftime("%B %d, %Y"),
    }
    # we want to have the ability to override the default receipt page when
    # there is only one item in the order
    if order_items.count() == 1:
        receipt_template = order_items[0].single_item_receipt_template
        context.update(order_items[0].single_item_receipt_context)

        # TODO (ECOM-188): Once the A/B test of separate verified / payment flow
        # completes, implement this in a more general way.  For now,
        # we simply redirect to the new receipt page (in verify_student).
        if settings.FEATURES.get('SEPARATE_VERIFICATION_FROM_PAYMENT') and request.session.get('separate-verified', False):
            if receipt_template == 'shoppingcart/verified_cert_receipt.html':
                url = reverse(
                    'verify_student_payment_confirmation',
                    kwargs={'course_id': unicode(order_items[0].course_id)}
                )

                # Add a query string param for the order ID
                # This allows the view to query for the receipt information later.
                url += '?payment-order-num={order_num}'.format(
                    order_num=order_items[0].order.id
                )
                return HttpResponseRedirect(url)

    return render_to_response(receipt_template, context)
Exemplo n.º 49
0
def _get_processor_exception_html(exception):
    """
    Return HTML indicating that an error occurred.

    Args:
        exception (CCProcessorException): The exception that occurred.

    Returns:
        unicode: The rendered HTML.

    """
    payment_support_email = microsite.get_value('payment_support_email', settings.PAYMENT_SUPPORT_EMAIL)
    if isinstance(exception, CCProcessorDataException):
        return _format_error_html(
            _(
                u"Sorry! Our payment processor sent us back a payment confirmation that had inconsistent data! "
                u"We apologize that we cannot verify whether the charge went through and take further action on your order. "
                u"The specific error message is: {msg} "
                u"Your credit card may possibly have been charged.  Contact us with payment-specific questions at {email}."
            ).format(
                msg=u'<span class="exception_msg">{msg}</span>'.format(msg=exception.message),
                email=payment_support_email
            )
        )
    elif isinstance(exception, CCProcessorWrongAmountException):
        return _format_error_html(
            _(
                u"Sorry! Due to an error your purchase was charged for a different amount than the order total! "
                u"The specific error message is: {msg}. "
                u"Your credit card has probably been charged. Contact us with payment-specific questions at {email}."
            ).format(
                msg=u'<span class="exception_msg">{msg}</span>'.format(msg=exception.message),
                email=payment_support_email
            )
        )
    elif isinstance(exception, CCProcessorSignatureException):
        return _format_error_html(
            _(
                u"Sorry! Our payment processor sent us back a corrupted message regarding your charge, so we are "
                u"unable to validate that the message actually came from the payment processor. "
                u"The specific error message is: {msg}. "
                u"We apologize that we cannot verify whether the charge went through and take further action on your order. "
                u"Your credit card may possibly have been charged. Contact us with payment-specific questions at {email}."
            ).format(
                msg=u'<span class="exception_msg">{msg}</span>'.format(msg=exception.message),
                email=payment_support_email
            )
        )
    elif isinstance(exception, CCProcessorUserCancelled):
        return _format_error_html(
            _(
                u"Sorry! Our payment processor sent us back a message saying that you have cancelled this transaction. "
                u"The items in your shopping cart will exist for future purchase. "
                u"If you feel that this is in error, please contact us with payment-specific questions at {email}."
            ).format(
                email=payment_support_email
            )
        )
    elif isinstance(exception, CCProcessorUserDeclined):
        return _format_error_html(
            _(
                u"We're sorry, but this payment was declined. The items in your shopping cart have been saved. "
                u"If you have any questions about this transaction, please contact us at {email}."
            ).format(
                email=payment_support_email
            )
        )
    else:
        return _format_error_html(
            _(
                u"Sorry! Your payment could not be processed because an unexpected exception occurred. "
                u"Please contact us at {email} for assistance."
            ).format(email=payment_support_email)
        )
Exemplo n.º 50
0
def learner_profile_context(request, profile_username, user_is_staff):
    """Context for the learner profile page.

    Args:
        logged_in_user (object): Logged In user.
        profile_username (str): username of user whose profile is requested.
        user_is_staff (bool): Logged In user has staff access.
        build_absolute_uri_func ():

    Returns:
        dict

    Raises:
        ObjectDoesNotExist: the specified profile_username does not exist.
    """
    profile_user = User.objects.get(username=profile_username)
    logged_in_user = request.user

    own_profile = (logged_in_user.username == profile_username)

    account_settings_data = get_account_settings(request, profile_username)

    preferences_data = get_user_preferences(profile_user, profile_username)

    context = {
        'data': {
            'profile_user_id':
            profile_user.id,
            'default_public_account_fields':
            settings.ACCOUNT_VISIBILITY_CONFIGURATION['public_fields'],
            'default_visibility':
            settings.ACCOUNT_VISIBILITY_CONFIGURATION['default_visibility'],
            'accounts_api_url':
            reverse("accounts_api", kwargs={'username': profile_username}),
            'preferences_api_url':
            reverse('preferences_api', kwargs={'username': profile_username}),
            'preferences_data':
            preferences_data,
            'account_settings_data':
            account_settings_data,
            'profile_image_upload_url':
            reverse('profile_image_upload',
                    kwargs={'username': profile_username}),
            'profile_image_remove_url':
            reverse('profile_image_remove',
                    kwargs={'username': profile_username}),
            'profile_image_max_bytes':
            settings.PROFILE_IMAGE_MAX_BYTES,
            'profile_image_min_bytes':
            settings.PROFILE_IMAGE_MIN_BYTES,
            'account_settings_page_url':
            reverse('account_settings'),
            'has_preferences_access':
            (logged_in_user.username == profile_username or user_is_staff),
            'own_profile':
            own_profile,
            'country_options':
            list(countries),
            'language_options':
            settings.ALL_LANGUAGES,
            'platform_name':
            microsite.get_value('platform_name', settings.PLATFORM_NAME),
        },
        'disable_courseware_js': True,
    }
    return context
Exemplo n.º 51
0
def checkout_receipt(request):
    """ Receipt view. """

    page_title = _('Receipt')
    is_payment_complete = True
    payment_support_email = microsite.get_value('payment_support_email',
                                                settings.PAYMENT_SUPPORT_EMAIL)
    payment_support_link = '<a href=\"mailto:{email}\">{email}</a>'.format(
        email=payment_support_email)

    is_cybersource = all(k in request.POST
                         for k in ('signed_field_names', 'decision',
                                   'reason_code'))
    if is_cybersource and request.POST['decision'] != 'ACCEPT':
        # Cybersource may redirect users to this view if it couldn't recover
        # from an error while capturing payment info.
        is_payment_complete = False
        page_title = _('Payment Failed')
        reason_code = request.POST['reason_code']
        # if the problem was with the info submitted by the user, we present more detailed messages.
        if is_user_payment_error(reason_code):
            error_summary = _(
                "There was a problem with this transaction. You have not been charged."
            )
            error_text = _(
                "Make sure your information is correct, or try again with a different card or another form of payment."
            )
        else:
            error_summary = _(
                "A system error occurred while processing your payment. You have not been charged."
            )
            error_text = _("Please wait a few minutes and then try again.")
        for_help_text = _("For help, contact {payment_support_link}.").format(
            payment_support_link=payment_support_link)
    else:
        # if anything goes wrong rendering the receipt, it indicates a problem fetching order data.
        error_summary = _("An error occurred while creating your receipt.")
        error_text = None  # nothing particularly helpful to say if this happens.
        for_help_text = _(
            "If your course does not appear on your dashboard, contact {payment_support_link}."
        ).format(payment_support_link=payment_support_link)

    commerce_configuration = CommerceConfiguration.current()
    # user order cache should be cleared when a new order is placed
    # so user can see new order in their order history.
    if is_payment_complete and commerce_configuration.enabled and commerce_configuration.is_cache_enabled:
        cache_key = commerce_configuration.CACHE_KEY + '.' + str(
            request.user.id)
        cache.delete(cache_key)

    context = {
        'page_title':
        page_title,
        'is_payment_complete':
        is_payment_complete,
        'platform_name':
        microsite.get_value('platform_name', settings.PLATFORM_NAME),
        'verified':
        SoftwareSecurePhotoVerification.verification_valid_or_pending(
            request.user).exists(),
        'error_summary':
        error_summary,
        'error_text':
        error_text,
        'for_help_text':
        for_help_text,
        'payment_support_email':
        payment_support_email,
        'username':
        request.user.username,
        'nav_hidden':
        True,
        'is_request_in_themed_site':
        is_request_in_themed_site()
    }
    return render_to_response('commerce/checkout_receipt.html', context)
Exemplo n.º 52
0
def _do_enroll_students(course, course_key, students, secure=False, overload=False, auto_enroll=False, email_students=False, is_shib_course=False):
    """
    Do the actual work of enrolling multiple students, presented as a string
    of emails separated by commas or returns
    `course` is course object
    `course_key` id of course (a CourseKey)
    `students` string of student emails separated by commas or returns (a `str`)
    `overload` un-enrolls all existing students (a `boolean`)
    `auto_enroll` is user input preference (a `boolean`)
    `email_students` is user input preference (a `boolean`)
    """

    new_students, new_students_lc = get_and_clean_student_list(students)
    status = dict([x, 'unprocessed'] for x in new_students)

    if overload:  # delete all but staff
        todelete = CourseEnrollment.objects.filter(course_id=course_key)
        for enrollee in todelete:
            if not has_access(enrollee.user, 'staff', course) and enrollee.user.email.lower() not in new_students_lc:
                status[enrollee.user.email] = 'deleted'
                enrollee.deactivate()
            else:
                status[enrollee.user.email] = 'is staff'
        ceaset = CourseEnrollmentAllowed.objects.filter(course_id=course_key)
        for cea in ceaset:
            status[cea.email] = 'removed from pending enrollment list'
        ceaset.delete()

    if email_students:
        protocol = 'https' if secure else 'http'
        stripped_site_name = microsite.get_value(
            'SITE_NAME',
            settings.SITE_NAME
        )
        # TODO: Use request.build_absolute_uri rather than '{proto}://{site}{path}'.format
        # and check with the Services team that this works well with microsites
        registration_url = '{proto}://{site}{path}'.format(
            proto=protocol,
            site=stripped_site_name,
            path=reverse('register_user')
        )
        course_url = '{proto}://{site}{path}'.format(
            proto=protocol,
            site=stripped_site_name,
            path=reverse('course_root', kwargs={'course_id': course_key.to_deprecated_string()})
        )
        # We can't get the url to the course's About page if the marketing site is enabled.
        course_about_url = None
        if not settings.FEATURES.get('ENABLE_MKTG_SITE', False):
            course_about_url = u'{proto}://{site}{path}'.format(
                proto=protocol,
                site=stripped_site_name,
                path=reverse('about_course', kwargs={'course_id': course_key.to_deprecated_string()})
            )

        # Composition of email
        email_data = {
            'site_name': stripped_site_name,
            'registration_url': registration_url,
            'course': course,
            'auto_enroll': auto_enroll,
            'course_url': course_url,
            'course_about_url': course_about_url,
            'is_shib_course': is_shib_course
        }

    for student in new_students:
        try:
            user = User.objects.get(email=student)
        except User.DoesNotExist:

            # Student not signed up yet, put in pending enrollment allowed table
            cea = CourseEnrollmentAllowed.objects.filter(email=student, course_id=course_key)

            # If enrollmentallowed already exists, update auto_enroll flag to however it was set in UI
            # Will be 0 or 1 records as there is a unique key on email + course_id
            if cea:
                cea[0].auto_enroll = auto_enroll
                cea[0].save()
                status[student] = 'user does not exist, enrollment already allowed, pending with auto enrollment ' \
                    + ('on' if auto_enroll else 'off')
                continue

            # EnrollmentAllowed doesn't exist so create it
            cea = CourseEnrollmentAllowed(email=student, course_id=course_key, auto_enroll=auto_enroll)
            cea.save()

            status[student] = 'user does not exist, enrollment allowed, pending with auto enrollment ' \
                + ('on' if auto_enroll else 'off')

            if email_students:
                # User is allowed to enroll but has not signed up yet
                email_data['email_address'] = student
                email_data['message'] = 'allowed_enroll'
                send_mail_ret = send_mail_to_student(student, email_data)
                status[student] += (', email sent' if send_mail_ret else '')
            continue

        # Student has already registered
        if CourseEnrollment.is_enrolled(user, course_key):
            status[student] = 'already enrolled'
            continue

        try:
            # Not enrolled yet
            CourseEnrollment.enroll(user, course_key)
            status[student] = 'added'

            if email_students:
                # User enrolled for first time, populate dict with user specific info
                email_data['email_address'] = student
                email_data['full_name'] = user.profile.name
                email_data['message'] = 'enrolled_enroll'
                send_mail_ret = send_mail_to_student(student, email_data)
                status[student] += (', email sent' if send_mail_ret else '')

        except Exception:  # pylint: disable=broad-except
            status[student] = 'rejected'

    datatable = {'header': ['StudentEmail', 'action']}
    datatable['data'] = [[x, status[x]] for x in sorted(status)]
    datatable['title'] = _('Enrollment of students')

    def sf(stat):  # pylint: disable=invalid-name
        return [x for x in status if status[x] == stat]

    data = dict(added=sf('added'), rejected=sf('rejected') + sf('exists'),
                deleted=sf('deleted'), datatable=datatable)

    return data
Exemplo n.º 53
0
def send_credit_notifications(username, course_key):
    """Sends email notification to user on different phases during credit
    course e.g., credit eligibility, credit payment etc.
    """
    try:
        user = User.objects.get(username=username)
    except User.DoesNotExist:
        log.error('No user with %s exist', username)
        return

    course = modulestore().get_course(course_key, depth=0)
    course_display_name = course.display_name
    tracking_context = tracker.get_tracker().resolve_context()
    tracking_id = str(tracking_context.get('user_id'))
    client_id = str(tracking_context.get('client_id'))
    events = '&t=event&ec=email&ea=open'
    tracking_pixel = 'https://www.google-analytics.com/collect?v=1&tid' + tracking_id + '&cid' + client_id + events
    dashboard_link = _email_url_parser('dashboard')
    credit_course_link = _email_url_parser('courses', '?type=credit')

    # get attached branded logo
    logo_image = cache.get('credit.email.attached-logo')
    if logo_image is None:
        branded_logo = {
            'title': 'Logo',
            'path': settings.NOTIFICATION_EMAIL_EDX_LOGO,
            'cid': str(uuid.uuid4())
        }
        logo_image_id = branded_logo['cid']
        logo_image = attach_image(branded_logo, 'Header Logo')
        if logo_image:
            cache.set('credit.email.attached-logo', logo_image,
                      settings.CREDIT_NOTIFICATION_CACHE_TIMEOUT)
    else:
        # strip enclosing angle brackets from 'logo_image' cache 'Content-ID'
        logo_image_id = logo_image.get('Content-ID', '')[1:-1]

    context = {
        'full_name': user.get_full_name(),
        'platform_name': settings.PLATFORM_NAME,
        'course_name': course_display_name,
        'branded_logo': logo_image_id,
        'dashboard_link': dashboard_link,
        'credit_course_link': credit_course_link,
        'tracking_pixel': tracking_pixel,
    }

    # create the root email message
    notification_msg = MIMEMultipart('related')
    # add 'alternative' part to root email message to encapsulate the plain and
    # HTML versions, so message agents can decide which they want to display.
    msg_alternative = MIMEMultipart('alternative')
    notification_msg.attach(msg_alternative)
    # render the credit notification templates
    subject = _(u'Course Credit Eligibility')

    # add alternative plain text message
    email_body_plain = render_to_string(
        'credit_notifications/credit_eligibility_email.txt', context)
    msg_alternative.attach(
        MIMEText(email_body_plain, _subtype='plain', _charset='utf-8'))

    # add alternative html message
    email_body_content = cache.get('credit.email.css-email-body')
    if email_body_content is None:
        html_file_path = file_path_finder(
            'templates/credit_notifications/credit_eligibility_email.html')
        if html_file_path:
            with open(html_file_path, 'r') as cur_file:
                cur_text = cur_file.read()
                # use html parser to unescape html characters which are changed
                # by the 'pynliner' while adding inline css to html content
                html_parser = HTMLParser.HTMLParser()
                email_body_content = html_parser.unescape(
                    with_inline_css(cur_text))
                # cache the email body content before rendering it since the
                # email context will change for each user e.g., 'full_name'
                cache.set('credit.email.css-email-body', email_body_content,
                          settings.CREDIT_NOTIFICATION_CACHE_TIMEOUT)
        else:
            email_body_content = ''

    email_body = Template(email_body_content).render([context])
    msg_alternative.attach(
        MIMEText(email_body, _subtype='html', _charset='utf-8'))

    # attach logo image
    if logo_image:
        notification_msg.attach(logo_image)

    # add email addresses of sender and receiver
    from_address = microsite.get_value('default_from_email',
                                       settings.DEFAULT_FROM_EMAIL)
    to_address = user.email

    # send the root email message
    msg = EmailMessage(subject, None, from_address, [to_address])
    msg.attach(notification_msg)
    msg.send()
Exemplo n.º 54
0
def mktg_course_about(request, course_id):
    """This is the button that gets put into an iframe on the Drupal site."""
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)

    try:
        permission_name = microsite.get_value(
            'COURSE_ABOUT_VISIBILITY_PERMISSION',
            settings.COURSE_ABOUT_VISIBILITY_PERMISSION)
        course = get_course_with_access(request.user, permission_name,
                                        course_key)
    except (ValueError, Http404):
        # If a course does not exist yet, display a "Coming Soon" button
        return render_to_response(
            'courseware/mktg_coming_soon.html',
            {'course_id': course_key.to_deprecated_string()})

    registered = registered_for_course(course, request.user)

    if has_access(request.user, 'load', course):
        course_target = reverse('info',
                                args=[course.id.to_deprecated_string()])
    else:
        course_target = reverse('about_course',
                                args=[course.id.to_deprecated_string()])

    allow_registration = has_access(request.user, 'enroll', course)

    show_courseware_link = (has_access(request.user, 'load', course)
                            or settings.FEATURES.get('ENABLE_LMS_MIGRATION'))
    course_modes = CourseMode.modes_for_course_dict(course.id)

    context = {
        'course': course,
        'registered': registered,
        'allow_registration': allow_registration,
        'course_target': course_target,
        'show_courseware_link': show_courseware_link,
        'course_modes': course_modes,
    }

    if settings.FEATURES.get('ENABLE_MKTG_EMAIL_OPT_IN'):
        # Drupal will pass organization names using a GET parameter, as follows:
        #     ?org=Harvard
        #     ?org=Harvard,MIT
        # If no full names are provided, the marketing iframe won't show the
        # email opt-in checkbox.
        org = request.GET.get('org')
        if org:
            org_list = org.split(',')
            # HTML-escape the provided organization names
            org_list = [cgi.escape(org) for org in org_list]
            if len(org_list) > 1:
                if len(org_list) > 2:
                    # Translators: The join of three or more institution names (e.g., Harvard, MIT, and Dartmouth).
                    org_name_string = _(
                        "{first_institutions}, and {last_institution}").format(
                            first_institutions=u", ".join(org_list[:-1]),
                            last_institution=org_list[-1])
                else:
                    # Translators: The join of two institution names (e.g., Harvard and MIT).
                    org_name_string = _(
                        "{first_institution} and {second_institution}").format(
                            first_institution=org_list[0],
                            second_institution=org_list[1])
            else:
                org_name_string = org_list[0]

            context['checkbox_label'] = ungettext(
                "I would like to receive email from {institution_series} and learn about its other programs.",
                "I would like to receive email from {institution_series} and learn about their other programs.",
                len(org_list)).format(institution_series=org_name_string)

    # The edx.org marketing site currently displays only in English.
    # To avoid displaying a different language in the register / access button,
    # we force the language to English.
    # However, OpenEdX installations with a different marketing front-end
    # may want to respect the language specified by the user or the site settings.
    force_english = settings.FEATURES.get('IS_EDX_DOMAIN', False)
    if force_english:
        translation.activate('en-us')

    try:
        return render_to_response('courseware/mktg_course_about.html', context)
    finally:
        # Just to be safe, reset the language if we forced it to be English.
        if force_english:
            translation.deactivate()
Exemplo n.º 55
0
def platform_name():
    """
    Django template tag that outputs the current platform name:
    {% platform_name %}
    """
    return microsite.get_value('platform_name', settings.PLATFORM_NAME)
Exemplo n.º 56
0
def _record_feedback_in_zendesk(realname,
                                email,
                                subject,
                                details,
                                tags,
                                additional_info,
                                group_name=None,
                                require_update=False):
    """
    Create a new user-requested Zendesk ticket.

    Once created, the ticket will be updated with a private comment containing
    additional information from the browser and server, such as HTTP headers
    and user state. Returns a boolean value indicating whether ticket creation
    was successful, regardless of whether the private comment update succeeded.

    If `group_name` is provided, attaches the ticket to the matching Zendesk group.

    If `require_update` is provided, returns False when the update does not
    succeed. This allows using the private comment to add necessary information
    which the user will not see in followup emails from support.
    """
    zendesk_api = _ZendeskApi()

    additional_info_string = (u"Additional information:\n\n" + u"\n".join(
        u"%s: %s" % (key, value)
        for (key, value) in additional_info.items() if value is not None))

    # Tag all issues with LMS to distinguish channel in Zendesk; requested by student support team
    zendesk_tags = list(tags.values()) + ["LMS"]

    # Per edX support, we would like to be able to route white label feedback items
    # via tagging
    white_label_org = microsite.get_value('course_org_filter')
    if white_label_org:
        zendesk_tags = zendesk_tags + [
            "whitelabel_{org}".format(org=white_label_org)
        ]

    new_ticket = {
        "ticket": {
            "requester": {
                "name": realname,
                "email": email
            },
            "subject": subject,
            "comment": {
                "body": details
            },
            "tags": zendesk_tags
        }
    }
    group = None
    if group_name is not None:
        group = zendesk_api.get_group(group_name)
        if group is not None:
            new_ticket['ticket']['group_id'] = group['id']
    try:
        ticket_id = zendesk_api.create_ticket(new_ticket)
        if group_name is not None and group is None:
            # Support uses Zendesk groups to track tickets. In case we
            # haven't been able to correctly group this ticket, log its ID
            # so it can be found later.
            log.warning(
                'Unable to find group named %s for Zendesk ticket with ID %s.',
                group_name, ticket_id)
    except zendesk.ZendeskError:
        log.exception("Error creating Zendesk ticket")
        return False

    # Additional information is provided as a private update so the information
    # is not visible to the user.
    ticket_update = {
        "ticket": {
            "comment": {
                "public": False,
                "body": additional_info_string
            }
        }
    }
    try:
        zendesk_api.update_ticket(ticket_id, ticket_update)
    except zendesk.ZendeskError:
        log.exception("Error updating Zendesk ticket with ID %s.", ticket_id)
        # The update is not strictly necessary, so do not indicate
        # failure to the user unless it has been requested with
        # `require_update`.
        if require_update:
            return False
    return True
Exemplo n.º 57
0
def register_code_redemption(request, registration_code):
    """
    This view allows the student to redeem the registration code
    and enroll in the course.
    """

    # Add some rate limiting here by re-using the RateLimitMixin as a helper class
    site_name = microsite.get_value('SITE_NAME', settings.SITE_NAME)
    limiter = BadRequestRateLimiter()
    if limiter.is_rate_limit_exceeded(request):
        AUDIT_LOG.warning("Rate limit exceeded in registration code redemption.")
        return HttpResponseForbidden()

    template_to_render = 'shoppingcart/registration_code_redemption.html'
    if request.method == "GET":
        reg_code_is_valid, reg_code_already_redeemed, course_registration = get_reg_code_validity(registration_code,
                                                                                                  request, limiter)
        course = get_course_by_id(getattr(course_registration, 'course_id'), depth=0)
        context = {
            'reg_code_already_redeemed': reg_code_already_redeemed,
            'reg_code_is_valid': reg_code_is_valid,
            'reg_code': registration_code,
            'site_name': site_name,
            'course': course,
            'registered_for_course': registered_for_course(course, request.user)
        }
        return render_to_response(template_to_render, context)
    elif request.method == "POST":
        reg_code_is_valid, reg_code_already_redeemed, course_registration = get_reg_code_validity(registration_code,
                                                                                                  request, limiter)
        course = get_course_by_id(getattr(course_registration, 'course_id'), depth=0)
        context = {
            'reg_code': registration_code,
            'site_name': site_name,
            'course': course,
            'reg_code_is_valid': reg_code_is_valid,
            'reg_code_already_redeemed': reg_code_already_redeemed,
        }
        if reg_code_is_valid and not reg_code_already_redeemed:
            # remove the course from the cart if it was added there.
            cart = Order.get_cart_for_user(request.user)
            try:
                cart_items = cart.find_item_by_course_id(course_registration.course_id)

            except ItemNotFoundInCartException:
                pass
            else:
                for cart_item in cart_items:
                    if isinstance(cart_item, PaidCourseRegistration) or isinstance(cart_item, CourseRegCodeItem):
                        cart_item.delete()

            #now redeem the reg code.
            redemption = RegistrationCodeRedemption.create_invoice_generated_registration_redemption(course_registration, request.user)
            try:
                kwargs = {}
                if course_registration.mode_slug is not None:
                    if CourseMode.mode_for_course(course.id, course_registration.mode_slug):
                        kwargs['mode'] = course_registration.mode_slug
                    else:
                        raise RedemptionCodeError()
                redemption.course_enrollment = CourseEnrollment.enroll(request.user, course.id, **kwargs)
                redemption.save()
                context['redemption_success'] = True
            except RedemptionCodeError:
                context['redeem_code_error'] = True
                context['redemption_success'] = False
            except EnrollmentClosedError:
                context['enrollment_closed'] = True
                context['redemption_success'] = False
            except CourseFullError:
                context['course_full'] = True
                context['redemption_success'] = False
            except AlreadyEnrolledError:
                context['registered_for_course'] = True
                context['redemption_success'] = False
        else:
            context['redemption_success'] = False
        return render_to_response(template_to_render, context)
Exemplo n.º 58
0
    def refund_cert_callback(sender, course_enrollment=None, **kwargs):
        """
        When a CourseEnrollment object calls its unenroll method, this function checks to see if that unenrollment
        occurred in a verified certificate that was within the refund deadline.  If so, it actually performs the
        refund.

        Returns the refunded certificate on a successful refund; else, it returns nothing.
        """

        # Only refund verified cert unenrollments that are within bounds of the expiration date
        if not course_enrollment.refundable():
            return

        target_certs = CertificateItem.objects.filter(
            course_id=course_enrollment.course_id,
            user_id=course_enrollment.user,
            status='purchased',
            mode='verified')
        try:
            target_cert = target_certs[0]
        except IndexError:
            log.error(
                "Matching CertificateItem not found while trying to refund.  User %s, Course %s",
                course_enrollment.user, course_enrollment.course_id)
            return
        target_cert.status = 'refunded'
        target_cert.refund_requested_time = datetime.now(pytz.utc)
        target_cert.save()
        target_cert.order.status = 'refunded'
        target_cert.order.save()

        order_number = target_cert.order_id
        # send billing an email so they can handle refunding
        subject = _("[Refund] User-Requested Refund")
        message = "User {user} ({user_email}) has requested a refund on Order #{order_number}.".format(
            user=course_enrollment.user,
            user_email=course_enrollment.user.email,
            order_number=order_number)
        to_email = [settings.PAYMENT_SUPPORT_EMAIL]
        from_email = microsite.get_value('payment_support_email',
                                         settings.PAYMENT_SUPPORT_EMAIL)
        try:
            send_mail(subject,
                      message,
                      from_email,
                      to_email,
                      fail_silently=False)
        except Exception as exception:  # pylint: disable=broad-except
            err_str = (
                'Failed sending email to billing to request a refund for verified certificate'
                ' (User {user}, Course {course}, CourseEnrollmentID {ce_id}, Order #{order})\n{exception}'
            )
            log.error(
                err_str.format(
                    user=course_enrollment.user,
                    course=course_enrollment.course_id,
                    ce_id=course_enrollment.id,
                    order=order_number,
                    exception=exception,
                ))

        return target_cert
Exemplo n.º 59
0
def send_mail_to_student(student, param_dict):
    """
    Construct the email using templates and then send it.
    `student` is the student's email address (a `str`),

    `param_dict` is a `dict` with keys
    [
        `site_name`: name given to edX instance (a `str`)
        `registration_url`: url for registration (a `str`)
        `course_id`: id of course (a `str`)
        `auto_enroll`: user input option (a `str`)
        `course_url`: url of course (a `str`)
        `email_address`: email of student (a `str`)
        `full_name`: student full name (a `str`)
        `message`: type of email to send and template to use (a `str`)
        `is_shib_course`: (a `boolean`)
    ]

    Returns a boolean indicating whether the email was sent successfully.
    """

    # add some helpers and microconfig subsitutions
    if 'course' in param_dict:
        param_dict['course_name'] = param_dict[
            'course'].display_name_with_default

    param_dict['site_name'] = microsite.get_value('SITE_NAME',
                                                  param_dict['site_name'])

    subject = None
    message = None

    # see if we are running in a microsite and that there is an
    # activation email template definition available as configuration, if so, then render that
    message_type = param_dict['message']

    email_template_dict = {
        'allowed_enroll': ('emails/enroll_email_allowedsubject.txt',
                           'emails/enroll_email_allowedmessage.txt'),
        'enrolled_enroll': ('emails/enroll_email_enrolledsubject.txt',
                            'emails/enroll_email_enrolledmessage.txt'),
        'allowed_unenroll': ('emails/unenroll_email_subject.txt',
                             'emails/unenroll_email_allowedmessage.txt'),
        'enrolled_unenroll': ('emails/unenroll_email_subject.txt',
                              'emails/unenroll_email_enrolledmessage.txt'),
        'add_beta_tester': ('emails/add_beta_tester_email_subject.txt',
                            'emails/add_beta_tester_email_message.txt'),
        'remove_beta_tester': ('emails/remove_beta_tester_email_subject.txt',
                               'emails/remove_beta_tester_email_message.txt'),
    }

    subject_template, message_template = email_template_dict.get(
        message_type, (None, None))
    if subject_template is not None and message_template is not None:
        subject = render_to_string(subject_template, param_dict)
        message = render_to_string(message_template, param_dict)

    if subject and message:
        # Remove leading and trailing whitespace from body
        message = message.strip()

        # Email subject *must not* contain newlines
        subject = ''.join(subject.splitlines())
        from_address = microsite.get_value('email_from_address',
                                           settings.DEFAULT_FROM_EMAIL)

        send_mail(subject,
                  message,
                  from_address, [student],
                  fail_silently=False)
Exemplo n.º 60
0
def render_html_view(request, user_id, course_id):
    """
    This public view generates an HTML representation of the specified user and course
    If a certificate is not available, we display a "Sorry!" screen instead
    """
    preview_mode = request.GET.get('preview', None)
    platform_name = microsite.get_value("platform_name",
                                        settings.PLATFORM_NAME)
    configuration = CertificateHtmlViewConfiguration.get_config()
    # Create the initial view context, bootstrapping with Django settings and passed-in values
    context = {}
    _update_context_with_basic_info(context, course_id, platform_name,
                                    configuration)
    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 course and user objects
    try:
        course_key = CourseKey.from_string(course_id)
        user = User.objects.get(id=user_id)
        course = modulestore().get_course(course_key)

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

    # Load user's certificate
    user_certificate = _get_user_certificate(request, user, course_key, course,
                                             preview_mode)
    if not user_certificate:
        return render_to_response(invalid_template_path, context)

    # 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)
    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 organization info
    _update_organization_context(context, course)

    # Append course info
    _update_course_context(request, context, course, platform_name)

    # Append user info
    _update_context_with_user_info(context, user, user_certificate)

    # Append social sharing info
    _update_social_context(request, context, course, user, user_certificate,
                           platform_name)

    # Append/Override the existing view context values with certificate specific values
    _update_certificate_context(context, user_certificate, platform_name)

    # Append badge info
    _update_badge_context(context, course, user)

    # Append microsite overrides
    _update_microsite_context(context, configuration)

    # Add certificate header/footer data to current context
    context.update(
        get_certificate_header_context(is_secure=request.is_secure()))
    context.update(get_certificate_footer_context())

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

    # Track certificate view events
    _track_certificate_events(request, context, course, user, user_certificate)

    # FINALLY, render appropriate certificate
    return _render_certificate_template(request, context, course,
                                        user_certificate)