Beispiel #1
0
def get_id_token(user):
    """
    Return a JWT for `user`, suitable for use with the course discovery service.

    Arguments:
        user (User): User for whom to generate the JWT.

    Returns:
        str: The JWT.
    """
    try:
        # Service users may not have user profiles.
        full_name = UserProfile.objects.get(user=user).name
    except UserProfile.DoesNotExist:
        full_name = None

    now = datetime.datetime.utcnow()
    expires_in = getattr(settings, 'OAUTH_ID_TOKEN_EXPIRATION', 30)

    payload = {
        'preferred_username': user.username,
        'name': full_name,
        'email': user.email,
        'administrator': user.is_staff,
        'iss': helpers.get_value('OAUTH_OIDC_ISSUER', settings.OAUTH_OIDC_ISSUER),
        'exp': now + datetime.timedelta(seconds=expires_in),
        'iat': now,
        'aud': helpers.get_value('JWT_AUTH', settings.JWT_AUTH)['JWT_AUDIENCE'],
        'sub': anonymous_id_for_user(user, None),
    }
    secret_key = helpers.get_value('JWT_AUTH', settings.JWT_AUTH)['JWT_SECRET_KEY']

    return jwt.encode(payload, secret_key)
Beispiel #2
0
def is_commerce_service_configured():
    """
    Return a Boolean indicating whether or not configuration is present to use
    the external commerce service.
    """
    ecommerce_api_url = helpers.get_value("ECOMMERCE_API_URL", settings.ECOMMERCE_API_URL)
    ecommerce_api_signing_key = helpers.get_value("ECOMMERCE_API_SIGNING_KEY", settings.ECOMMERCE_API_SIGNING_KEY)
    return bool(ecommerce_api_url and ecommerce_api_signing_key)
Beispiel #3
0
def is_commerce_service_configured():
    """
    Return a Boolean indicating whether or not configuration is present to use
    the external commerce service.
    """
    ecommerce_api_url = helpers.get_value("ECOMMERCE_API_URL",
                                          settings.ECOMMERCE_API_URL)
    ecommerce_api_signing_key = helpers.get_value(
        "ECOMMERCE_API_SIGNING_KEY", settings.ECOMMERCE_API_SIGNING_KEY)
    return bool(ecommerce_api_url and ecommerce_api_signing_key)
Beispiel #4
0
def ecommerce_api_client(user):
    """ Returns an E-Commerce API client setup with authentication for the specified user. """
    return EdxRestApiClient(
        helpers.get_value("ECOMMERCE_API_URL", settings.ECOMMERCE_API_URL),
        helpers.get_value("ECOMMERCE_API_SIGNING_KEY", settings.ECOMMERCE_API_SIGNING_KEY),
        user.username,
        user.profile.name if hasattr(user, 'profile') else None,
        user.email,
        tracking_context=create_tracking_context(user),
        issuer=settings.JWT_ISSUER,
        expires_in=settings.JWT_EXPIRATION
    )
Beispiel #5
0
def ecommerce_api_client(user):
    """ Returns an E-Commerce API client setup with authentication for the specified user. """
    jwt_auth = helpers.get_value("JWT_AUTH", settings.JWT_AUTH)
    return EdxRestApiClient(
        helpers.get_value("ECOMMERCE_API_URL", settings.ECOMMERCE_API_URL),
        helpers.get_value("ECOMMERCE_API_SIGNING_KEY",
                          settings.ECOMMERCE_API_SIGNING_KEY),
        user.username,
        user.profile.name if hasattr(user, 'profile') else None,
        user.email,
        tracking_context=create_tracking_context(user),
        issuer=jwt_auth['JWT_ISSUER'],
        expires_in=jwt_auth['JWT_EXPIRATION'])
Beispiel #6
0
def request_password_change(email, orig_host, is_secure):
    """Email a single-use link for performing a password reset.

    Users must confirm the password change before we update their information.

    Args:
        email (str): An email address
        orig_host (str): An originating host, extracted from a request with get_host
        is_secure (bool): Whether the request was made with HTTPS

    Returns:
        None

    Raises:
        UserNotFound
        AccountRequestError
        UserAPIInternalError: the operation failed due to an unexpected error.
    """
    # Binding data to a form requires that the data be passed as a dictionary
    # to the Form class constructor.
    form = PasswordResetFormNoActive({'email': email})

    # Validate that a user exists with the given email address.
    if form.is_valid():
        # Generate a single-use link for performing a password reset
        # and email it to the user.
        form.save(from_email=theming_helpers.get_value(
            'default_from_email', settings.DEFAULT_FROM_EMAIL),
                  domain_override=orig_host,
                  use_https=is_secure)
    else:
        # No user with the provided email address exists.
        raise UserNotFound
    def test_reset_password_email(self, send_email):
        """Tests contents of reset password email, and that user is not active"""

        good_req = self.request_factory.post('/password_reset/', {'email': self.user.email})
        good_req.user = self.user
        good_resp = password_reset(good_req)
        self.assertEquals(good_resp.status_code, 200)
        obj = json.loads(good_resp.content)
        self.assertEquals(obj, {
            'success': True,
            'value': "('registration/password_reset_done.html', [])",
        })

        (subject, msg, from_addr, to_addrs) = send_email.call_args[0]
        self.assertIn("Password reset", subject)
        self.assertIn("You're receiving this e-mail because you requested a password reset", msg)
        self.assertEquals(from_addr, theming_helpers.get_value('email_from_address', settings.DEFAULT_FROM_EMAIL))
        self.assertEquals(len(to_addrs), 1)
        self.assertIn(self.user.email, to_addrs)

        self.assert_event_emitted(
            SETTING_CHANGE_INITIATED, user_id=self.user.id, setting=u'password', old=None, new=None,
        )

        #test that the user is not active
        self.user = User.objects.get(pk=self.user.pk)
        self.assertFalse(self.user.is_active)
        re.search(r'password_reset_confirm/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/', msg).groupdict()
Beispiel #8
0
    def test_reset_password_email(self, send_email):
        """Tests contents of reset password email, and that user is not active"""

        good_req = self.request_factory.post('/password_reset/', {'email': self.user.email})
        good_req.user = self.user
        good_resp = password_reset(good_req)
        self.assertEquals(good_resp.status_code, 200)
        obj = json.loads(good_resp.content)
        self.assertEquals(obj, {
            'success': True,
            'value': "('registration/password_reset_done.html', [])",
        })

        (subject, msg, from_addr, to_addrs) = send_email.call_args[0]
        self.assertIn("Password reset", subject)
        self.assertIn("You're receiving this e-mail because you requested a password reset", msg)
        self.assertEquals(from_addr, theming_helpers.get_value('default_from_email', settings.DEFAULT_FROM_EMAIL))
        self.assertEquals(len(to_addrs), 1)
        self.assertIn(self.user.email, to_addrs)

        self.assert_event_emitted(
            SETTING_CHANGE_INITIATED, user_id=self.user.id, setting=u'password', old=None, new=None,
        )

        #test that the user is not active
        self.user = User.objects.get(pk=self.user.pk)
        self.assertFalse(self.user.is_active)
        re.search(r'password_reset_confirm/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/', msg).groupdict()
Beispiel #9
0
    def _generate_jwt(self, user, scopes, expires_in):
        """ Returns a JWT access token. """
        now = int(time())
        jwt_auth = helpers.get_value("JWT_AUTH", settings.JWT_AUTH)
        payload = {
            'iss': jwt_auth['JWT_ISSUER'],
            'aud': jwt_auth['JWT_AUDIENCE'],
            'exp': now + expires_in,
            'iat': now,
            'preferred_username': user.username,
            'scopes': scopes,
        }

        for scope in scopes:
            handler = self.claim_handlers.get(scope)

            if handler:
                handler(payload, user)

        secret = jwt_auth['JWT_SECRET_KEY']
        token = jwt.encode(payload,
                           secret,
                           algorithm=jwt_auth['JWT_ALGORITHM'])

        return token
Beispiel #10
0
def request_password_change(email, orig_host, is_secure):
    """Email a single-use link for performing a password reset.

    Users must confirm the password change before we update their information.

    Args:
        email (str): An email address
        orig_host (str): An originating host, extracted from a request with get_host
        is_secure (bool): Whether the request was made with HTTPS

    Returns:
        None

    Raises:
        UserNotFound
        AccountRequestError
        UserAPIInternalError: the operation failed due to an unexpected error.
    """
    # Binding data to a form requires that the data be passed as a dictionary
    # to the Form class constructor.
    form = PasswordResetFormNoActive({'email': email})

    # Validate that a user exists with the given email address.
    if form.is_valid():
        # Generate a single-use link for performing a password reset
        # and email it to the user.
        form.save(
            from_email=theming_helpers.get_value('default_from_email', settings.DEFAULT_FROM_EMAIL),
            domain_override=orig_host,
            use_https=is_secure
        )
    else:
        # No user with the provided email address exists.
        raise UserNotFound
def _get_course_email_context(course):
    """
    Returns context arguments to apply to all emails, independent of recipient.
    """
    course_id = course.id.to_deprecated_string()
    course_title = course.display_name
    course_end_date = get_default_time_display(course.end)
    course_url = 'https://{}{}'.format(
        settings.SITE_NAME,
        reverse('course_root', kwargs={'course_id': course_id}))
    image_url = u'https://{}{}'.format(settings.SITE_NAME,
                                       course_image_url(course))
    email_context = {
        'course_title':
        course_title,
        'course_url':
        course_url,
        'course_image_url':
        image_url,
        'course_end_date':
        course_end_date,
        'account_settings_url':
        'https://{}{}'.format(settings.SITE_NAME, reverse('account_settings')),
        'email_settings_url':
        'https://{}{}'.format(settings.SITE_NAME, reverse('dashboard')),
        'platform_name':
        theming_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME),
    }
    return email_context
Beispiel #12
0
def course_discovery_api_client(user):
    """ Returns a Course Discovery API client setup with authentication for the specified user. """
    course_discovery_client = Client.objects.get(name=CLIENT_NAME)
    secret_key = helpers.get_value('JWT_AUTH', settings.JWT_AUTH)['JWT_SECRET_KEY']
    return EdxRestApiClient(
        course_discovery_client.url,
        jwt=get_id_token(user, CLIENT_NAME, secret_key=secret_key)
    )
Beispiel #13
0
    def payment_page_url(self):
        """ Return the URL for the checkout page.

        Example:
            http://localhost:8002/basket/single_item/
        """
        ecommerce_url_root = helpers.get_value('ECOMMERCE_PUBLIC_URL_ROOT', settings.ECOMMERCE_PUBLIC_URL_ROOT)
        return urljoin(ecommerce_url_root, self.config.single_course_checkout_page)
Beispiel #14
0
def course_discovery_api_client(user):
    """ Returns a Course Discovery API client setup with authentication for the specified user. """
    course_discovery_client = Client.objects.get(name=CLIENT_NAME)
    secret_key = helpers.get_value('JWT_AUTH',
                                   settings.JWT_AUTH)['JWT_SECRET_KEY']
    return EdxRestApiClient(course_discovery_client.url,
                            jwt=get_id_token(user,
                                             CLIENT_NAME,
                                             secret_key=secret_key))
 def test_get_value_returns_override(self):
     """
     Tests to make sure the get_value() operation returns a combined dictionary consisting
     of the base container with overridden keys from the microsite configuration
     """
     with patch('microsite_configuration.microsite.get_value') as mock_get_value:
         override_key = 'JWT_ISSUER'
         override_value = 'testing'
         mock_get_value.return_value = {override_key: override_value}
         jwt_auth = helpers.get_value('JWT_AUTH')
         self.assertEqual(jwt_auth[override_key], override_value)
Beispiel #16
0
def generate_refund_notification_body(student, refund_ids):  # pylint: disable=invalid-name
    """ Returns a refund notification message body. """
    msg = _(
        "A refund request has been initiated for {username} ({email}). "
        "To process this request, please visit the link(s) below."
    ).format(username=student.username, email=student.email)

    ecommerce_url_root = get_value('ECOMMERCE_PUBLIC_URL_ROOT', settings.ECOMMERCE_PUBLIC_URL_ROOT)
    refund_urls = [urljoin(ecommerce_url_root, '/dashboard/refunds/{}/'.format(refund_id))
                   for refund_id in refund_ids]

    return '{msg}\n\n{urls}'.format(msg=msg, urls='\n'.join(refund_urls))
Beispiel #17
0
 def test_get_value_returns_override(self):
     """
     Tests to make sure the get_value() operation returns a combined dictionary consisting
     of the base container with overridden keys from the microsite configuration
     """
     with patch('microsite_configuration.microsite.get_value'
                ) as mock_get_value:
         override_key = 'JWT_ISSUER'
         override_value = 'testing'
         mock_get_value.return_value = {override_key: override_value}
         jwt_auth = helpers.get_value('JWT_AUTH')
         self.assertEqual(jwt_auth[override_key], override_value)
Beispiel #18
0
def generate_refund_notification_body(student, refund_ids):  # pylint: disable=invalid-name
    """ Returns a refund notification message body. """
    msg = _("A refund request has been initiated for {username} ({email}). "
            "To process this request, please visit the link(s) below.").format(
                username=student.username, email=student.email)

    ecommerce_url_root = get_value('ECOMMERCE_PUBLIC_URL_ROOT',
                                   settings.ECOMMERCE_PUBLIC_URL_ROOT)
    refund_urls = [
        urljoin(ecommerce_url_root, '/dashboard/refunds/{}/'.format(refund_id))
        for refund_id in refund_ids
    ]

    return '{msg}\n\n{urls}'.format(msg=msg, urls='\n'.join(refund_urls))
Beispiel #19
0
 def format_address(course_title_no_quotes):
     """
     Partial function for formatting the from_addr. Since
     `course_title_no_quotes` may be truncated to make sure the returned
     string has fewer than 320 characters, we define this function to make
     it easy to determine quickly what the max length is for
     `course_title_no_quotes`.
     """
     return from_addr_format.format(
         course_title=course_title_no_quotes,
         course_name=course_name,
         from_email=theming_helpers.get_value(
             'bulk_email_default_from_email',
             settings.BULK_EMAIL_DEFAULT_FROM_EMAIL))
Beispiel #20
0
    def assertEmailUser(self, email_user, subject_template, subject_context, body_template, body_context):
        """Assert that `email_user` was used to send and email with the supplied subject and body

        `email_user`: The mock `django.contrib.auth.models.User.email_user` function
            to verify
        `subject_template`: The template to have been used for the subject
        `subject_context`: The context to have been used for the subject
        `body_template`: The template to have been used for the body
        `body_context`: The context to have been used for the body
        """
        email_user.assert_called_with(
            mock_render_to_string(subject_template, subject_context),
            mock_render_to_string(body_template, body_context),
            theming_helpers.get_value('email_from_address', settings.DEFAULT_FROM_EMAIL)
        )
Beispiel #21
0
    def assertEmailUser(self, email_user, subject_template, subject_context, body_template, body_context):
        """Assert that `email_user` was used to send and email with the supplied subject and body

        `email_user`: The mock `django.contrib.auth.models.User.email_user` function
            to verify
        `subject_template`: The template to have been used for the subject
        `subject_context`: The context to have been used for the subject
        `body_template`: The template to have been used for the body
        `body_context`: The context to have been used for the body
        """
        email_user.assert_called_with(
            mock_render_to_string(subject_template, subject_context),
            mock_render_to_string(body_template, body_context),
            theming_helpers.get_value('default_from_email', settings.DEFAULT_FROM_EMAIL)
        )
Beispiel #22
0
 def format_address(course_title_no_quotes):
     """
     Partial function for formatting the from_addr. Since
     `course_title_no_quotes` may be truncated to make sure the returned
     string has fewer than 320 characters, we define this function to make
     it easy to determine quickly what the max length is for
     `course_title_no_quotes`.
     """
     return from_addr_format.format(
         course_title=course_title_no_quotes,
         course_name=course_name,
         from_email=theming_helpers.get_value(
             'bulk_email_default_from_email',
             settings.BULK_EMAIL_DEFAULT_FROM_EMAIL
         )
     )
 def save(self,
          domain_override=None,
          subject_template_name='registration/password_reset_subject.txt',
          email_template_name='registration/password_reset_email.html',
          use_https=False,
          token_generator=default_token_generator,
          from_email=theming_helpers.get_value('default_from_email',
                                               settings.DEFAULT_FROM_EMAIL),
          request=None):
     """
     Generates a one-use only link for resetting password and sends to the
     user.
     """
     # This import is here because we are copying and modifying the .save from Django 1.4.5's
     # django.contrib.auth.forms.PasswordResetForm directly, which has this import in this place.
     from django.core.mail import send_mail
     for user in self.users_cache:
         if not domain_override:
             site_name = microsite.get_value('SITE_NAME',
                                             settings.SITE_NAME)
         else:
             site_name = domain_override
         context = {
             'email':
             user.email,
             'site_name':
             site_name,
             'uid':
             int_to_base36(user.id),
             'user':
             user,
             'token':
             token_generator.make_token(user),
             'protocol':
             'https' if use_https else 'http',
             'platform_name':
             microsite.get_value('platform_name', settings.PLATFORM_NAME)
         }
         subject = loader.render_to_string(subject_template_name, context)
         # Email subject *must not* contain newlines
         subject = subject.replace('\n', '')
         email = loader.render_to_string(email_template_name, context)
         send_mail(subject, email, from_email, [user.email])
Beispiel #24
0
 def test_email_success(self, send_mail):
     """ Test email was sent if no errors encountered. """
     old_email = self.user.email
     new_email = "*****@*****.**"
     registration_key = "test registration key"
     self.assertIsNone(self.do_email_change(self.user, new_email, registration_key))
     context = {
         'key': registration_key,
         'old_email': old_email,
         'new_email': new_email
     }
     send_mail.assert_called_with(
         mock_render_to_string('emails/email_change_subject.txt', context),
         mock_render_to_string('emails/email_change.txt', context),
         theming_helpers.get_value('email_from_address', settings.DEFAULT_FROM_EMAIL),
         [new_email]
     )
     self.assert_event_emitted(
         SETTING_CHANGE_INITIATED, user_id=self.user.id, setting=u'email', old=old_email, new=new_email
     )
Beispiel #25
0
 def test_email_success(self, send_mail):
     """ Test email was sent if no errors encountered. """
     old_email = self.user.email
     new_email = "*****@*****.**"
     registration_key = "test registration key"
     self.assertIsNone(self.do_email_change(self.user, new_email, registration_key))
     context = {
         'key': registration_key,
         'old_email': old_email,
         'new_email': new_email
     }
     send_mail.assert_called_with(
         mock_render_to_string('emails/email_change_subject.txt', context),
         mock_render_to_string('emails/email_change.txt', context),
         theming_helpers.get_value('default_from_email', settings.DEFAULT_FROM_EMAIL),
         [new_email]
     )
     self.assert_event_emitted(
         SETTING_CHANGE_INITIATED, user_id=self.user.id, setting=u'email', old=old_email, new=new_email
     )
Beispiel #26
0
    def _generate_jwt(self, user, scopes, expires_in):
        """ Returns a JWT access token. """
        now = int(time())
        jwt_auth = helpers.get_value("JWT_AUTH", settings.JWT_AUTH)
        payload = {
            'iss': jwt_auth['JWT_ISSUER'],
            'aud': jwt_auth['JWT_AUDIENCE'],
            'exp': now + expires_in,
            'iat': now,
            'preferred_username': user.username,
        }

        for scope in scopes:
            handler = self.claim_handlers.get(scope)

            if handler:
                handler(payload, user)

        secret = jwt_auth['JWT_SECRET_KEY']
        token = jwt.encode(payload, secret, algorithm=jwt_auth['JWT_ALGORITHM'])

        return token
Beispiel #27
0
 def save(
         self,
         domain_override=None,
         subject_template_name='registration/password_reset_subject.txt',
         email_template_name='registration/password_reset_email.html',
         use_https=False,
         token_generator=default_token_generator,
         from_email=theming_helpers.get_value('default_from_email', settings.DEFAULT_FROM_EMAIL),
         request=None
 ):
     """
     Generates a one-use only link for resetting password and sends to the
     user.
     """
     # This import is here because we are copying and modifying the .save from Django 1.4.5's
     # django.contrib.auth.forms.PasswordResetForm directly, which has this import in this place.
     from django.core.mail import send_mail
     for user in self.users_cache:
         if not domain_override:
             site_name = microsite.get_value(
                 'SITE_NAME',
                 settings.SITE_NAME
             )
         else:
             site_name = domain_override
         context = {
             'email': user.email,
             'site_name': site_name,
             'uid': int_to_base36(user.id),
             'user': user,
             'token': token_generator.make_token(user),
             'protocol': 'https' if use_https else 'http',
             'platform_name': microsite.get_value('platform_name', settings.PLATFORM_NAME)
         }
         subject = loader.render_to_string(subject_template_name, context)
         # Email subject *must not* contain newlines
         subject = subject.replace('\n', '')
         email = loader.render_to_string(email_template_name, context)
         send_mail(subject, email, from_email, [user.email])
Beispiel #28
0
def _get_course_email_context(course):
    """
    Returns context arguments to apply to all emails, independent of recipient.
    """
    course_id = course.id.to_deprecated_string()
    course_title = course.display_name
    course_end_date = get_default_time_display(course.end)
    course_url = 'https://{}{}'.format(
        settings.SITE_NAME,
        reverse('course_root', kwargs={'course_id': course_id})
    )
    image_url = u'https://{}{}'.format(settings.SITE_NAME, course_image_url(course))
    email_context = {
        'course_title': course_title,
        'course_url': course_url,
        'course_image_url': image_url,
        'course_end_date': course_end_date,
        'account_settings_url': 'https://{}{}'.format(settings.SITE_NAME, reverse('account_settings')),
        'email_settings_url': 'https://{}{}'.format(settings.SITE_NAME, reverse('dashboard')),
        'platform_name': theming_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME),
    }
    return email_context
Beispiel #29
0
    def save(
        self,
        domain_override=None,
        subject_template_name="registration/password_reset_subject.txt",
        email_template_name="registration/password_reset_email.html",
        use_https=False,
        token_generator=default_token_generator,
        from_email=theming_helpers.get_value("email_from_address", settings.DEFAULT_FROM_EMAIL),
        request=None,
    ):
        """
        Generates a one-use only link for resetting password and sends to the
        user.
        """
        # This import is here because we are copying and modifying the .save from Django 1.4.5's
        # django.contrib.auth.forms.PasswordResetForm directly, which has this import in this place.
        from django.core.mail import send_mail

        for user in self.users_cache:
            if not domain_override:
                site_name = microsite.get_value("SITE_NAME", settings.SITE_NAME)
            else:
                site_name = domain_override
            context = {
                "email": user.email,
                "site_name": site_name,
                "uid": int_to_base36(user.id),
                "user": user,
                "token": token_generator.make_token(user),
                "protocol": "https" if use_https else "http",
                "platform_name": microsite.get_value("platform_name", settings.PLATFORM_NAME),
            }
            subject = loader.render_to_string(subject_template_name, context)
            # Email subject *must not* contain newlines
            subject = subject.replace("\n", "")
            email = loader.render_to_string(email_template_name, context)
            send_mail(subject, email, from_email, [user.email])
Beispiel #30
0
 def __init__(self, user, asymmetric=False, secret=None):
     self.user = user
     self.asymmetric = asymmetric
     self.secret = secret
     self.jwt_auth = helpers.get_value('JWT_AUTH', settings.JWT_AUTH)
Beispiel #31
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]

    providers_names = get_credit_provider_display_names(course_key)
    providers_string = make_providers_strings(providers_names)
    context = {
        "full_name": user.get_full_name(),
        "platform_name": theming_helpers.get_value("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,
        "providers": providers_string,
    }

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

    if providers_string:
        subject = _(u"You are eligible for credit from {providers_string}").format(providers_string=providers_string)

    # add alternative plain text message
    email_body_plain = render_to_string("credit_notifications/credit_eligibility_email.txt", context)
    msg_alternative.attach(SafeMIMEText(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(SafeMIMEText(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 = theming_helpers.get_value("email_from_address", 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()
Beispiel #32
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]

    providers_names = get_credit_provider_display_names(course_key)
    providers_string = make_providers_strings(providers_names)
    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,
        'providers': providers_string,
    }

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

    if providers_string:
        subject = _(u'You are eligible for credit from {providers_string}').format(
            providers_string=providers_string
        )

    # add alternative plain text message
    email_body_plain = render_to_string('credit_notifications/credit_eligibility_email.txt', context)
    msg_alternative.attach(SafeMIMEText(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(SafeMIMEText(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 = theming_helpers.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()
Beispiel #33
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]

    providers_names = get_credit_provider_display_names(course_key)
    providers_string = make_providers_strings(providers_names)
    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,
        'providers': providers_string,
    }

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

    if providers_string:
        subject = _(u'You are eligible for credit from {providers_string}'
                    ).format(providers_string=providers_string)

    # add alternative plain text message
    email_body_plain = render_to_string(
        'credit_notifications/credit_eligibility_email.txt', context)
    msg_alternative.attach(
        SafeMIMEText(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(
        SafeMIMEText(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 = theming_helpers.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()
def send_mail_to_student(student, param_dict, language=None):
    """
    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`)
        `display_name` : display name of a course (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`)
    ]

    `language` is the language used to render the email. If None the language
    of the currently-logged in user (that is, the user sending the email) will
    be used.

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

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

    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'
        ),
        'account_creation_and_enrollment': (
            'emails/enroll_email_enrolledsubject.txt',
            'emails/account_creation_and_enroll_emailMessage.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, message = render_message_to_string(
            subject_template, message_template, param_dict, language=language
        )

    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 = theming_helpers.get_value(
            'email_from_address',
            settings.DEFAULT_FROM_EMAIL
        )

        send_mail(subject, message, from_address, [student], fail_silently=False)
Beispiel #35
0
def send_mail_to_student(student, param_dict, language=None):
    """
    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`)
        `display_name` : display name of a course (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`)
    ]

    `language` is the language used to render the email. If None the language
    of the currently-logged in user (that is, the user sending the email) will
    be used.

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

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

    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'),
        'account_creation_and_enrollment':
        ('emails/enroll_email_enrolledsubject.txt',
         'emails/account_creation_and_enroll_emailMessage.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, message = render_message_to_string(subject_template,
                                                    message_template,
                                                    param_dict,
                                                    language=language)

    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 = theming_helpers.get_value('email_from_address',
                                                 settings.DEFAULT_FROM_EMAIL)

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