def test_mako(self): # The default_filters used here have to match the ones in edxmako. template = Template( """ <%! from util.markup import HTML, ugettext as _ %> ${_(u"A & {BC}").format(BC=HTML("B & C"))} """, default_filters=['decode.utf8', 'h'], ) out = template.render({}) self.assertEqual(out.strip(), u"A & B & C")
def _render_valid_certificate(request, context, custom_template=None): if custom_template: template = Template( custom_template.template, output_encoding='utf-8', input_encoding='utf-8', default_filters=['decode.utf8'], encoding_errors='replace', ) context = RequestContext(request, context) return HttpResponse(template.render(context)) else: return render_to_response("certificates/valid.html", context)
def load_template(self, template_name, template_dirs=None): source, file_path = self.load_template_source(template_name, template_dirs) # In order to allow dynamic template overrides, we need to cache templates based on their absolute paths # rather than relative paths, overriding templates would have same relative paths. module_directory = self.module_directory.rstrip( "/") + "/{dir_hash}/".format(dir_hash=hash(file_path)) if source.startswith("## mako\n"): # This is a mako template template = Template(filename=file_path, module_directory=module_directory, input_encoding='utf-8', output_encoding='utf-8', default_filters=['decode.utf8'], encoding_errors='replace', uri=template_name, engine=engines['mako']) return template, None else: # This is a regular template try: template = Engine.get_default().from_string(source) return template, None except ImproperlyConfigured: # Either no DjangoTemplates engine was configured -or- multiple engines # were configured, making the get_default() call above fail. raise except TemplateDoesNotExist: # If compiling the loaded template raises TemplateDoesNotExist, back off to # returning the source and display name for the requested template. # This allows for eventual correct identification of the actual template that does # not exist. return source, file_path
def load_template(self, template_name, template_dirs=None): source, file_path = self.load_template_source(template_name, template_dirs) if source.startswith("## mako\n"): # This is a mako template template = Template(filename=file_path, module_directory=self.module_directory, input_encoding='utf-8', output_encoding='utf-8', uri=template_name) return template, None else: # This is a regular template origin = make_origin(file_path, self.load_template_source, template_name, template_dirs) try: template = get_template_from_string(source, origin, template_name) return template, None except TemplateDoesNotExist: # If compiling the template we found raises TemplateDoesNotExist, back off to # returning the source and display name for the template we were asked to load. # This allows for correct identification (later) of the actual template that does # not exist. return source, file_path
def load_template(self, template_name, template_dirs=None): source, file_path = self.load_template_source(template_name, template_dirs) if source.startswith("## mako\n"): # This is a mako template template = Template(filename=file_path, module_directory=self.module_directory, input_encoding='utf-8', output_encoding='utf-8', uri=template_name) return template, None else: # This is a regular template try: template = Engine.get_default().from_string(source) return template, None except ImproperlyConfigured: # Either no DjangoTemplates engine was configured -or- multiple engines # were configured, making the get_default() call above fail. raise except TemplateDoesNotExist: # If compiling the loaded template raises TemplateDoesNotExist, back off to # returning the source and display name for the requested template. # This allows for eventual correct identification of the actual template that does # not exist. return source, file_path
def _render_certificate_template(request, context, course, user_certificate): """ Picks appropriate certificate templates and renders it. """ if settings.FEATURES.get('CUSTOM_CERTIFICATE_TEMPLATES_ENABLED', False): custom_template = get_certificate_template(course.id, user_certificate.mode) if custom_template: template = Template( custom_template, output_encoding='utf-8', input_encoding='utf-8', default_filters=['decode.utf8'], encoding_errors='replace', ) context = RequestContext(request, context) return HttpResponse(template.render(context)) return render_to_response("certificates/valid.html", context)
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(u'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_attribute_values( course_key, 'display_name') providers_string = make_providers_strings(providers_names) context = { 'full_name': user.get_full_name(), 'platform_name': configuration_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 = six.moves.html_parser.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 = configuration_helpers.get_value('email_from_address', settings.DEFAULT_FROM_EMAIL) to_address = user.email # send the root email message msg = EmailMessage(subject, '', from_address, [to_address]) msg.attach(notification_msg) msg.send()
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'] = '© {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 & characters represent an ampersand character and can be ignored context['company_tos_urltext'] = _("Terms of Service & 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)
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'] = '© {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 & characters represent an ampersand character and can be ignored context['company_tos_urltext'] = _("Terms of Service & 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)