Commit a3211a74 by Chris Dodge Committed by Don Mitchell

Introduction of the Microsite feature which allows for limited multi-tenant…

Introduction of the Microsite feature which allows for limited multi-tenant branding on a subdomain basis, e.g. foo.edx.org and bar.edx.org

fix errorenous logic when running a microsite that could reside in a hosting environment with a marketing site in front of it

pep8/pylint fixes

address PR feedback, remove underscore from test hostname

more pep8/pylint cleanup. Skip test_microsites test, it works on localdev, not on Jenkins. Need to talk with QA team

manually add Ned's single-to-double quote fix

change aws.py runtimes so that the microsite_dir that is read from configuration is changed to a python path

Conflicts:
	lms/templates/help_modal.html
parent a86086b7
......@@ -53,6 +53,8 @@ from xmodule.modulestore.locator import BlockUsageLocator
from course_creators.views import get_course_creator_status, add_user_with_status_unrequested
from contentstore import utils
from microsite_configuration.middleware import MicrositeConfiguration
__all__ = ['course_info_handler', 'course_handler', 'course_info_update_handler',
'settings_handler',
'grading_handler',
......@@ -413,15 +415,21 @@ def settings_handler(request, tag=None, package_id=None, branch=None, version_gu
if 'text/html' in request.META.get('HTTP_ACCEPT', '') and request.method == 'GET':
upload_asset_url = locator.url_reverse('assets/')
# see if the ORG of this course can be attributed to a 'Microsite'. In that case, the
# course about page should be editable in Studio
about_page_editable = not MicrositeConfiguration.get_microsite_configuration_value_for_org(
course_module.location.org,
'ENABLE_MKTG_SITE',
settings.FEATURES.get('ENABLE_MKTG_SITE', False)
)
return render_to_response('settings.html', {
'context_course': course_module,
'course_locator': locator,
'lms_link_for_about_page': utils.get_lms_link_for_about_page(course_module.location),
'course_image_url': utils.course_image_url(course_module),
'details_url': locator.url_reverse('/settings/details/'),
'about_page_editable': not settings.FEATURES.get(
'ENABLE_MKTG_SITE', False
),
'about_page_editable': about_page_editable,
'upload_asset_url': upload_asset_url
})
elif 'application/json' in request.META.get('HTTP_ACCEPT', ''):
......
......@@ -10,6 +10,8 @@ from edxmako.shortcuts import render_to_response
from external_auth.views import ssl_login_shortcut
from microsite_configuration.middleware import MicrositeConfiguration
__all__ = ['signup', 'login_page', 'howitworks']
......@@ -29,10 +31,14 @@ def login_page(request):
Display the login form.
"""
csrf_token = csrf(request)['csrf_token']
return render_to_response('login.html', {
'csrf': csrf_token,
'forgot_password_link': "//{base}/login#forgot-password-modal".format(base=settings.LMS_BASE),
})
return render_to_response(
'login.html',
{
'csrf': csrf_token,
'forgot_password_link': "//{base}/login#forgot-password-modal".format(base=settings.LMS_BASE),
'platform_name': MicrositeConfiguration.get_microsite_configuration_value('platform_name', settings.PLATFORM_NAME),
}
)
def howitworks(request):
......
......@@ -9,6 +9,7 @@ This is the default template for our main set of AWS servers.
import json
from .common import *
from logsettings import get_logger_config
import os
......@@ -145,7 +146,6 @@ COURSES_WITH_UNSAFE_CODE = ENV_TOKENS.get("COURSES_WITH_UNSAFE_CODE", [])
#Timezone overrides
TIME_ZONE = ENV_TOKENS.get('TIME_ZONE', TIME_ZONE)
ENV_FEATURES = ENV_TOKENS.get('FEATURES', ENV_TOKENS.get('MITX_FEATURES', {}))
for feature, value in ENV_FEATURES.items():
FEATURES[feature] = value
......@@ -213,3 +213,16 @@ BROKER_URL = "{0}://{1}:{2}@{3}/{4}".format(CELERY_BROKER_TRANSPORT,
# Event tracking
TRACKING_BACKENDS.update(AUTH_TOKENS.get("TRACKING_BACKENDS", {}))
SUBDOMAIN_BRANDING = ENV_TOKENS.get('SUBDOMAIN_BRANDING', {})
VIRTUAL_UNIVERSITIES = ENV_TOKENS.get('VIRTUAL_UNIVERSITIES', [])
MICROSITE_CONFIGURATION = ENV_TOKENS.get('MICROSITE_CONFIGURATION', {})
MICROSITE_ROOT_DIR = ENV_TOKENS.get('MICROSITE_ROOT_DIR')
if len(MICROSITE_CONFIGURATION.keys()) > 0:
enable_microsites(
MICROSITE_CONFIGURATION,
SUBDOMAIN_BRANDING,
VIRTUAL_UNIVERSITIES,
microsites_root=path(MICROSITE_ROOT_DIR)
)
......@@ -25,7 +25,7 @@ Longer TODO:
import sys
import lms.envs.common
from lms.envs.common import USE_TZ, TECH_SUPPORT_EMAIL, PLATFORM_NAME, BUGS_EMAIL, DOC_STORE_CONFIG
from lms.envs.common import USE_TZ, TECH_SUPPORT_EMAIL, PLATFORM_NAME, BUGS_EMAIL, DOC_STORE_CONFIG, enable_microsites
from path import path
from lms.lib.xblock.mixin import LmsBlockMixin
......
"""
This is a localdev test for the Microsite processing pipeline
"""
# We intentionally define lots of variables that aren't used, and
# want to import all variables from base settings files
# pylint: disable=W0401, W0614
from .dev import *
from .dev import SUBDOMAIN_BRANDING, VIRTUAL_UNIVERSITIES
MICROSITE_NAMES = ['openedx']
MICROSITE_CONFIGURATION = {}
if MICROSITE_NAMES and len(MICROSITE_NAMES) > 0:
enable_microsites(MICROSITE_NAMES, MICROSITE_CONFIGURATION, SUBDOMAIN_BRANDING, VIRTUAL_UNIVERSITIES)
......@@ -16,6 +16,8 @@ from django.template import Context
from django.http import HttpResponse
import logging
from microsite_configuration.middleware import MicrositeConfiguration
import edxmako
import edxmako.middleware
from django.conf import settings
......@@ -71,6 +73,10 @@ def marketing_link_context_processor(request):
def render_to_string(template_name, dictionary, context=None, namespace='main'):
# see if there is an override template defined in the microsite
template_name = MicrositeConfiguration.get_microsite_template_path(template_name)
context_instance = Context(dictionary)
# add dictionary to context_instance
context_instance.update(dictionary or {})
......@@ -98,5 +104,9 @@ def render_to_response(template_name, dictionary=None, context_instance=None, na
Returns a HttpResponse whose content is filled with the result of calling
lookup.get_template(args[0]).render with the passed arguments.
"""
# see if there is an override template defined in the microsite
template_name = MicrositeConfiguration.get_microsite_template_path(template_name)
dictionary = dictionary or {}
return HttpResponse(render_to_string(template_name, dictionary, context_instance, namespace), **kwargs)
"""
This file implements the initial Microsite support for the Open edX platform.
A microsite enables the following features:
1) Mapping of sub-domain name to a 'brand', e.g. foo-university.edx.org
2) Present a landing page with a listing of courses that are specific to the 'brand'
3) Ability to swap out some branding elements in the website
"""
import threading
import os.path
from django.conf import settings
_microsite_configuration_threadlocal = threading.local()
_microsite_configuration_threadlocal.data = {}
def has_microsite_configuration_set():
"""
Returns whether the MICROSITE_CONFIGURATION has been set in the configuration files
"""
return getattr(settings, "MICROSITE_CONFIGURATION", False)
class MicrositeConfiguration(object):
"""
Middleware class which will bind configuration information regarding 'microsites' on a per request basis.
The actual configuration information is taken from Django settings information
"""
@classmethod
def is_request_in_microsite(cls):
"""
This will return if current request is a request within a microsite
"""
return cls.get_microsite_configuration()
@classmethod
def get_microsite_configuration(cls):
"""
Returns the current request's microsite configuration
"""
if not hasattr(_microsite_configuration_threadlocal, 'data'):
return {}
return _microsite_configuration_threadlocal.data
@classmethod
def get_microsite_configuration_value(cls, val_name, default=None):
"""
Returns a value associated with the request's microsite, if present
"""
configuration = cls.get_microsite_configuration()
return configuration.get(val_name, default)
@classmethod
def get_microsite_template_path(cls, relative_path):
"""
Returns a path to a Mako template, which can either be in
a microsite directory (as an override) or will just return what is passed in
"""
if not cls.is_request_in_microsite():
return relative_path
microsite_template_path = cls.get_microsite_configuration_value('template_dir')
if microsite_template_path:
search_path = microsite_template_path / relative_path
if os.path.isfile(search_path):
path = '{0}/templates/{1}'.format(
cls.get_microsite_configuration_value('microsite_name'),
relative_path
)
return path
return relative_path
@classmethod
def get_microsite_configuration_value_for_org(cls, org, val_name, default=None):
"""
This returns a configuration value for a microsite which has an org_filter that matches
what is passed in
"""
if not has_microsite_configuration_set():
return default
for key in settings.MICROSITE_CONFIGURATION.keys():
org_filter = settings.MICROSITE_CONFIGURATION[key].get('course_org_filter', None)
if org_filter == org:
return settings.MICROSITE_CONFIGURATION[key].get(val_name, default)
return default
@classmethod
def get_all_microsite_orgs(cls):
"""
This returns a set of orgs that are considered within a Microsite. This can be used,
for example, to do filtering
"""
org_filter_set = []
if not has_microsite_configuration_set():
return org_filter_set
for key in settings.MICROSITE_CONFIGURATION:
org_filter = settings.MICROSITE_CONFIGURATION[key].get('course_org_filter')
if org_filter:
org_filter_set.append(org_filter)
return org_filter_set
def clear_microsite_configuration(self):
"""
Clears out any microsite configuration from the current request/thread
"""
_microsite_configuration_threadlocal.data = {}
def process_request(self, request):
"""
Middleware entry point on every request processing. This will associate a request's domain name
with a 'University' and any corresponding microsite configuration information
"""
self.clear_microsite_configuration()
domain = request.META.get('HTTP_HOST', None)
if domain:
subdomain = MicrositeConfiguration.pick_subdomain(domain, settings.SUBDOMAIN_BRANDING.keys())
university = MicrositeConfiguration.match_university(subdomain)
microsite_configuration = self.get_microsite_configuration_for_university(university)
if microsite_configuration:
microsite_configuration['university'] = university
microsite_configuration['subdomain'] = subdomain
microsite_configuration['site_domain'] = domain
_microsite_configuration_threadlocal.data = microsite_configuration
# also put the configuration on the request itself to make it easier to dereference
request.microsite_configuration = _microsite_configuration_threadlocal.data
return None
def process_response(self, request, response):
"""
Middleware entry point for request completion.
"""
self.clear_microsite_configuration()
return response
def get_microsite_configuration_for_university(self, university):
"""
For a given university, return the microsite configuration which
is in the Django settings
"""
if not university:
return None
if not has_microsite_configuration_set():
return None
configuration = settings.MICROSITE_CONFIGURATION.get(university, None)
return configuration
@classmethod
def match_university(cls, domain):
"""
Return the university name specified for the domain, or None
if no university was specified
"""
if not settings.FEATURES['SUBDOMAIN_BRANDING'] or domain is None:
return None
subdomain = cls.pick_subdomain(domain, settings.SUBDOMAIN_BRANDING.keys())
return settings.SUBDOMAIN_BRANDING.get(subdomain)
@classmethod
def pick_subdomain(cls, domain, options, default='default'):
"""
Attempt to match the incoming request's HOST domain with a configuration map
to see what subdomains are supported in Microsites.
"""
for option in options:
if domain.startswith(option):
return option
return default
......@@ -71,6 +71,7 @@ from pytz import UTC
from util.json_request import JsonResponse
from microsite_configuration.middleware import MicrositeConfiguration
log = logging.getLogger("edx.student")
AUDIT_LOG = logging.getLogger("audit")
......@@ -250,7 +251,11 @@ def signin_user(request):
context = {
'course_id': request.GET.get('course_id'),
'enrollment_action': request.GET.get('enrollment_action')
'enrollment_action': request.GET.get('enrollment_action'),
'platform_name': MicrositeConfiguration.get_microsite_configuration_value(
'platform_name',
settings.PLATFORM_NAME
),
}
return render_to_response('login.html', context)
......@@ -269,7 +274,11 @@ def register_user(request, extra_context=None):
context = {
'course_id': request.GET.get('course_id'),
'enrollment_action': request.GET.get('enrollment_action')
'enrollment_action': request.GET.get('enrollment_action'),
'platform_name': MicrositeConfiguration.get_microsite_configuration_value(
'platform_name',
settings.PLATFORM_NAME
),
}
if extra_context is not None:
context.update(extra_context)
......@@ -311,9 +320,33 @@ def dashboard(request):
# longer exist (because the course IDs have changed). Still, we don't delete those
# enrollments, because it could have been a data push snafu.
course_enrollment_pairs = []
# for microsites, we want to filter and only show enrollments for courses within
# the microsites 'ORG'
course_org_filter = MicrositeConfiguration.get_microsite_configuration_value('course_org_filter')
# Let's filter out any courses in an "org" that has been declared to be
# in a Microsite
org_filter_out_set = MicrositeConfiguration.get_all_microsite_orgs()
# remove our current Microsite from the "filter out" list, if applicable
if course_org_filter:
org_filter_out_set.remove(course_org_filter)
for enrollment in CourseEnrollment.enrollments_for_user(user):
try:
course_enrollment_pairs.append((course_from_id(enrollment.course_id), enrollment))
course = course_from_id(enrollment.course_id)
# if we are in a Microsite, then filter out anything that is not
# attributed (by ORG) to that Microsite
if course_org_filter and course_org_filter != course.location.org:
continue
# Conversely, if we are not in a Microsite, then let's filter out any enrollments
# with courses attributed (by ORG) to Microsites
elif course.location.org in org_filter_out_set:
continue
course_enrollment_pairs.append((course, enrollment))
except ItemNotFoundError:
log.error("User {0} enrolled in non-existent course {1}"
.format(user.username, enrollment.course_id))
......@@ -539,7 +572,11 @@ def accounts_login(request):
course_id = _parse_course_id_from_string(redirect_to)
if course_id and _get_course_enrollment_domain(course_id):
return external_auth.views.course_specific_login(request, course_id)
return render_to_response('login.html')
context = {
'platform_name': settings.PLATFORM_NAME,
}
return render_to_response('login.html', context)
# Need different levels of logging
......@@ -899,26 +936,31 @@ def create_account(request, post_override=None):
return ret
(user, profile, registration) = ret
d = {'name': post_vars['name'],
'key': registration.activation_key,
}
context = {
'name': post_vars['name'],
'key': registration.activation_key,
}
# composes activation email
subject = render_to_string('emails/activation_email_subject.txt', d)
subject = render_to_string('emails/activation_email_subject.txt', context)
# Email subject *must not* contain newlines
subject = ''.join(subject.splitlines())
message = render_to_string('emails/activation_email.txt', d)
message = render_to_string('emails/activation_email.txt', context)
# don't send email if we are doing load testing or random user generation for some reason
if not (settings.FEATURES.get('AUTOMATIC_AUTH_FOR_TESTING')):
from_address = MicrositeConfiguration.get_microsite_configuration_value(
'email_from_address',
settings.DEFAULT_FROM_EMAIL
)
try:
if settings.FEATURES.get('REROUTE_ACTIVATION_EMAIL'):
dest_addr = settings.FEATURES['REROUTE_ACTIVATION_EMAIL']
message = ("Activation for %s (%s): %s\n" % (user, user.email, profile.name) +
'-' * 80 + '\n\n' + message)
send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [dest_addr], fail_silently=False)
send_mail(subject, message, from_address, [dest_addr], fail_silently=False)
else:
_res = user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL)
_res = user.email_user(subject, message, from_address)
except:
log.warning('Unable to send activation email to user', exc_info=True)
js['value'] = _('Could not send activation e-mail.')
......@@ -1171,15 +1213,23 @@ def change_email_request(request):
return HttpResponse(json.dumps({'success': False,
'error': _('Old email is the same as the new email.')}))
d = {'key': pec.activation_key,
'old_email': user.email,
'new_email': pec.new_email}
context = {
'key': pec.activation_key,
'old_email': user.email,
'new_email': pec.new_email
}
subject = render_to_string('emails/email_change_subject.txt', d)
subject = render_to_string('emails/email_change_subject.txt', context)
subject = ''.join(subject.splitlines())
message = render_to_string('emails/email_change.txt', d)
_res = send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [pec.new_email])
message = render_to_string('emails/email_change.txt', context)
from_address = MicrositeConfiguration.get_microsite_configuration_value(
'email_from_address',
settings.DEFAULT_FROM_EMAIL
)
_res = send_mail(subject, message, from_address, [pec.new_email])
return HttpResponse(json.dumps({'success': True}))
......
""" Utility functions related to HTTP requests """
from django.conf import settings
from microsite_configuration.middleware import MicrositeConfiguration
def safe_get_host(request):
......@@ -14,4 +15,4 @@ def safe_get_host(request):
if isinstance(settings.ALLOWED_HOSTS, (list, tuple)) and '*' not in settings.ALLOWED_HOSTS:
return request.get_host()
else:
return settings.SITE_NAME
return MicrositeConfiguration.get_microsite_configuration_value('site_domain', settings.SITE_NAME)
......@@ -2,15 +2,10 @@ from xmodule.modulestore.django import modulestore
from xmodule.course_module import CourseDescriptor
from django.conf import settings
from microsite_configuration.middleware import MicrositeConfiguration
def pick_subdomain(domain, options, default='default'):
for option in options:
if domain.startswith(option):
return option
return default
def get_visible_courses(domain=None):
def get_visible_courses():
"""
Return the set of CourseDescriptors that should be visible in this branded instance
"""
......@@ -20,31 +15,52 @@ def get_visible_courses(domain=None):
if isinstance(c, CourseDescriptor)]
courses = sorted(courses, key=lambda course: course.number)
if domain and settings.FEATURES.get('SUBDOMAIN_COURSE_LISTINGS'):
subdomain = pick_subdomain(domain, settings.COURSE_LISTINGS.keys())
visible_ids = frozenset(settings.COURSE_LISTINGS[subdomain])
return [course for course in courses if course.id in visible_ids]
subdomain = MicrositeConfiguration.get_microsite_configuration_value('subdomain')
# 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
if hasattr(settings, 'COURSE_LISTINGS') and subdomain in settings.COURSE_LISTINGS:
filtered_visible_ids = frozenset(settings.COURSE_LISTINGS[subdomain])
filtered_by_org = MicrositeConfiguration.get_microsite_configuration_value('course_org_filter')
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:
return courses
# Let's filter out any courses in an "org" that has been declared to be
# in a Microsite
org_filter_out_set = MicrositeConfiguration.get_all_microsite_orgs()
return [course for course in courses if course.location.org not in org_filter_out_set]
def get_university(domain=None):
def get_university_for_request():
"""
Return the university name specified for the domain, or None
if no university was specified
"""
if not settings.FEATURES['SUBDOMAIN_BRANDING'] or domain is None:
return None
return MicrositeConfiguration.get_microsite_configuration_value('university')
subdomain = pick_subdomain(domain, settings.SUBDOMAIN_BRANDING.keys())
return settings.SUBDOMAIN_BRANDING.get(subdomain)
def get_logo_url(domain=None):
def get_logo_url():
"""
Return the url for the branded logo image to be used
"""
university = get_university(domain)
# if the MicrositeConfiguration has a value for the logo_image_url
# let's use that
image_url = MicrositeConfiguration.get_microsite_configuration_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 = MicrositeConfiguration.get_microsite_configuration_value('university')
if university is None:
return '{static_url}images/header-logo.png'.format(
......
......@@ -6,8 +6,9 @@ from django_future.csrf import ensure_csrf_cookie
from edxmako.shortcuts import render_to_response
import student.views
import branding
import courseware.views
from microsite_configuration.middleware import MicrositeConfiguration
from edxmako.shortcuts import marketing_link
from util.cache import cache_if_anonymous
......@@ -25,10 +26,19 @@ def index(request):
if settings.FEATURES.get('AUTH_USE_MIT_CERTIFICATES'):
from external_auth.views import ssl_login
return ssl_login(request)
if settings.FEATURES.get('ENABLE_MKTG_SITE'):
enable_mktg_site = MicrositeConfiguration.get_microsite_configuration_value(
'ENABLE_MKTG_SITE',
settings.FEATURES.get('ENABLE_MKTG_SITE', False)
)
if enable_mktg_site:
return redirect(settings.MKTG_URLS.get('ROOT'))
university = branding.get_university(request.META.get('HTTP_HOST'))
university = MicrositeConfiguration.match_university(request.META.get('HTTP_HOST'))
# keep specialized logic for Edge until we can migrate over Edge to fully use
# microsite definitions
if university == 'edge':
context = {
'suppress_toplevel_navigation': True
......@@ -49,7 +59,9 @@ def courses(request):
to that. Otherwise, if subdomain branding is on, this is the university
profile page. Otherwise, it's the edX courseware.views.courses page
"""
if settings.FEATURES.get('ENABLE_MKTG_SITE', False):
enable_mktg_site = settings.FEATURES.get('ENABLE_MKTG_SITE') or MicrositeConfiguration.get_microsite_configuration_value('ENABLE_MKTG_SITE', False)
if enable_mktg_site:
return redirect(marketing_link('COURSES'), permanent=True)
if not settings.FEATURES.get('COURSES_ARE_BROWSABLE'):
......
......@@ -294,7 +294,7 @@ def get_courses(user, domain=None):
'''
Returns a list of courses available, sorted by course.number
'''
courses = branding.get_visible_courses(domain)
courses = branding.get_visible_courses()
courses = [c for c in courses if has_access(user, c, 'see_exists')]
courses = sorted(courses, key=lambda course: course.number)
......
"""
Tests related to the Microsites feature
"""
from django.core.urlresolvers import reverse
from django.test.utils import override_settings
from unittest import skip
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from helpers import LoginEnrollmentTestCase
from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE
MICROSITE_TEST_HOSTNAME = 'testmicrosite.testserver'
@override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE)
class TestMicrosites(ModuleStoreTestCase, LoginEnrollmentTestCase):
"""
This is testing of the Microsite feature
"""
STUDENT_INFO = [('view@test.com', 'foo'), ('view2@test.com', 'foo')]
def setUp(self):
# use a different hostname to test Microsites since they are
# triggered on subdomain mappings
#
# NOTE: The Microsite Configuration is in lms/envs/test.py. The content for the Test Microsite is in
# test_microsites/test_microsite.
#
# IMPORTANT: For these tests to work, this domain must be defined via
# DNS configuration (either local or published)
self.course = CourseFactory.create(display_name='Robot_Super_Course', org='TestMicrositeX')
self.chapter0 = ItemFactory.create(parent_location=self.course.location,
display_name='Overview')
self.chapter9 = ItemFactory.create(parent_location=self.course.location,
display_name='factory_chapter')
self.section0 = ItemFactory.create(parent_location=self.chapter0.location,
display_name='Welcome')
self.section9 = ItemFactory.create(parent_location=self.chapter9.location,
display_name='factory_section')
self.course_outside_microsite = CourseFactory.create(display_name='Robot_Course_Outside_Microsite', org='FooX')
def create_test_accounts(self):
"""
Build out the test accounts we'll use in these tests
"""
# Create student accounts and activate them.
for i in range(len(self.STUDENT_INFO)):
email, password = self.STUDENT_INFO[i]
username = 'u{0}'.format(i)
self.create_account(username, email, password)
self.activate_user(email)
@skip # skipping - runs fine on localdev, not jenkins environment
def test_microsite_anonymous_homepage_content(self):
"""
Verify that the homepage, when accessed via a Microsite domain, returns
HTML that reflects the Microsite branding elements
"""
resp = self.client.get('/', HTTP_HOST=MICROSITE_TEST_HOSTNAME)
self.assertEqual(resp.status_code, 200)
# assert various branding definitions on this Microsite
# as per the configuration and Microsite overrides
self.assertContains(resp, 'This is a Test Microsite Overlay') # Overlay test message
self.assertContains(resp, 'test_microsite/images/header-logo.png') # logo swap
self.assertContains(resp, 'test_microsite/css/test_microsite.css') # css override
self.assertContains(resp, '<title>Test Microsite</title>') # page title
# assert that test course display name is visible
self.assertContains(resp, 'Robot_Super_Course')
# assert that test course that is outside microsite is not visible
self.assertNotContains(resp, 'Robot_Course_Outside_Microsite')
# assert that footer template has been properly overriden on homepage
self.assertContains(resp, 'This is a Test Microsite footer')
# assert that the edX partners section is not in the HTML
self.assertNotContains(resp, '<section class="university-partners university-partners2x6">')
# assert that the edX partners tag line is not in the HTML
self.assertNotContains(resp, 'Explore free courses from')
@skip # skipping - runs fine on localdev, not jenkins environment
def test_not_microsite_anonymous_homepage_content(self):
"""
Make sure we see the right content on the homepage if we are not in a microsite
"""
resp = self.client.get('/')
self.assertEqual(resp.status_code, 200)
# assert various branding definitions on this Microsite ARE NOT VISIBLE
self.assertNotContains(resp, 'This is a Test Microsite Overlay') # Overlay test message
self.assertNotContains(resp, 'test_microsite/images/header-logo.png') # logo swap
self.assertNotContains(resp, 'test_microsite/css/test_microsite.css') # css override
self.assertNotContains(resp, '<title>Test Microsite</title>') # page title
# assert that test course display name IS NOT VISIBLE, since that is a Microsite only course
self.assertNotContains(resp, 'Robot_Super_Course')
# assert that test course that is outside microsite IS VISIBLE
self.assertContains(resp, 'Robot_Course_Outside_Microsite')
# assert that footer template has been properly overriden on homepage
self.assertNotContains(resp, 'This is a Test Microsite footer')
# assert that the edX partners section is not in the HTML
self.assertContains(resp, '<section class="university-partners university-partners2x6">')
# assert that the edX partners tag line is not in the HTML
self.assertContains(resp, 'Explore free courses from')
@skip # skipping - runs fine on localdev, not jenkins environment
def test_microsite_course_enrollment(self):
"""
Enroll user in a course scoped in a Microsite and one course outside of a Microsite
and make sure that they are only visible in the right Dashboards
"""
self.create_test_accounts()
email, password = self.STUDENT_INFO[0]
self.login(email, password)
self.enroll(self.course, True)
self.enroll(self.course_outside_microsite, True)
# Access the microsite dashboard and make sure the right courses appear
resp = self.client.get(reverse('dashboard'), HTTP_HOST=MICROSITE_TEST_HOSTNAME)
self.assertContains(resp, 'Robot_Super_Course')
self.assertNotContains(resp, 'Robot_Course_Outside_Microsite')
# Now access the non-microsite dashboard and make sure the right courses appear
resp = self.client.get(reverse('dashboard'))
self.assertNotContains(resp, 'Robot_Super_Course')
self.assertContains(resp, 'Robot_Course_Outside_Microsite')
......@@ -14,6 +14,8 @@ from student.models import CourseEnrollment, CourseEnrollmentAllowed
from courseware.models import StudentModule
from edxmako.shortcuts import render_to_string
from microsite_configuration.middleware import MicrositeConfiguration
# For determining if a shibboleth course
SHIBBOLETH_DOMAIN_PREFIX = 'shib:'
......@@ -223,22 +225,58 @@ def send_mail_to_student(student, param_dict):
Returns a boolean indicating whether the email was sent successfully.
"""
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 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'] = MicrositeConfiguration.get_microsite_configuration_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'
)
}
subject_template, message_template = email_template_dict.get(param_dict['message'], (None, None))
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())
send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [student], fail_silently=False)
from_address = MicrositeConfiguration.get_microsite_configuration_value(
'email_from_address',
settings.DEFAULT_FROM_EMAIL
)
send_mail(subject, message, from_address, [student], fail_silently=False)
def uses_shib(course):
......
......@@ -63,6 +63,8 @@ from xblock.fields import ScopeIds
from django.utils.translation import ugettext as _u
from lms.lib.xblock.runtime import handler_prefix
from microsite_configuration.middleware import MicrositeConfiguration
log = logging.getLogger(__name__)
# internal commands for managing forum roles:
......@@ -1282,7 +1284,10 @@ def _do_enroll_students(course, course_id, students, overload=False, auto_enroll
ceaset.delete()
if email_students:
stripped_site_name = settings.SITE_NAME
stripped_site_name = MicrositeConfiguration.get_microsite_configuration_value(
'SITE_NAME',
settings.SITE_NAME
)
registration_url = 'https://' + stripped_site_name + reverse('student.views.register_user')
#Composition of email
d = {'site_name': stripped_site_name,
......@@ -1291,7 +1296,7 @@ def _do_enroll_students(course, course_id, students, overload=False, auto_enroll
'auto_enroll': auto_enroll,
'course_url': 'https://' + stripped_site_name + '/courses/' + course_id,
'course_about_url': 'https://' + stripped_site_name + '/courses/' + course_id + '/about',
'is_shib_course': is_shib_course,
'is_shib_course': is_shib_course
}
for student in new_students:
......@@ -1373,7 +1378,10 @@ def _do_unenroll_students(course_id, students, email_students=False):
old_students, _ = get_and_clean_student_list(students)
status = dict([x, 'unprocessed'] for x in old_students)
stripped_site_name = settings.SITE_NAME
stripped_site_name = MicrositeConfiguration.get_microsite_configuration_value(
'SITE_NAME',
settings.SITE_NAME
)
if email_students:
course = course_from_id(course_id)
#Composition of email
......@@ -1447,22 +1455,43 @@ def send_mail_to_student(student, param_dict):
Returns a boolean indicating whether the email was sent successfully.
"""
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 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'] = MicrositeConfiguration.get_microsite_configuration_value(
'SITE_NAME',
param_dict.get('site_name', '')
)
subject = None
message = None
message_type = param_dict['message']
subject_template, message_template = EMAIL_TEMPLATE_DICT.get(param_dict['message'], (None, None))
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())
send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [student], fail_silently=False)
from_address = MicrositeConfiguration.get_microsite_configuration_value(
'email_from_address',
settings.DEFAULT_FROM_EMAIL
)
send_mail(subject, message, from_address, [student], fail_silently=False)
return True
else:
......
......@@ -32,6 +32,8 @@ from verify_student.models import SoftwareSecurePhotoVerification
from .exceptions import (InvalidCartItem, PurchasedCallbackException, ItemAlreadyInCartException,
AlreadyEnrolledInCourseException, CourseDoesNotExistException)
from microsite_configuration.middleware import MicrositeConfiguration
log = logging.getLogger("shoppingcart")
ORDER_STATUSES = (
......@@ -161,14 +163,22 @@ class Order(models.Model):
# 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']
})
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 = MicrositeConfiguration.get_microsite_configuration_value(
'email_from_address',
settings.DEFAULT_FROM_EMAIL
)
send_mail(subject, message,
settings.DEFAULT_FROM_EMAIL, [self.user.email]) # pylint: disable=E1101
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
......@@ -523,7 +533,10 @@ class CertificateItem(OrderItem):
user_email=course_enrollment.user.email,
order_number=order_number)
to_email = [settings.PAYMENT_SUPPORT_EMAIL]
from_email = [settings.PAYMENT_SUPPORT_EMAIL]
from_email = [MicrositeConfiguration.get_microsite_configuration_value(
'payment_support_email',
settings.PAYMENT_SUPPORT_EMAIL
)]
try:
send_mail(subject, message, from_email, to_email, fail_silently=False)
except (smtplib.SMTPException, BotoServerError):
......
......@@ -337,3 +337,13 @@ VERIFY_STUDENT = AUTH_TOKENS.get("VERIFY_STUDENT", VERIFY_STUDENT)
GRADES_DOWNLOAD_ROUTING_KEY = HIGH_MEM_QUEUE
GRADES_DOWNLOAD = ENV_TOKENS.get("GRADES_DOWNLOAD", GRADES_DOWNLOAD)
MICROSITE_CONFIGURATION = ENV_TOKENS.get('MICROSITE_CONFIGURATION', {})
MICROSITE_ROOT_DIR = ENV_TOKENS.get('MICROSITE_ROOT_DIR')
if MICROSITE_CONFIGURATION:
enable_microsites(
MICROSITE_CONFIGURATION,
SUBDOMAIN_BRANDING,
VIRTUAL_UNIVERSITIES,
microsites_root=path(MICROSITE_ROOT_DIR)
)
"""
This is a localdev test for the Microsite processing pipeline
"""
# We intentionally define lots of variables that aren't used, and
# want to import all variables from base settings files
# pylint: disable=W0401, W0614
from .dev import *
from .dev import SUBDOMAIN_BRANDING, VIRTUAL_UNIVERSITIES
MICROSITE_CONFIGURATION = {
"openedx": {
"domain_prefix": "openedx",
"university": "openedx",
"platform_name": "Open edX",
"logo_image_url": "openedx/images/header-logo.png",
"email_from_address": "openedx@edx.org",
"payment_support_email": "openedx@edx.org",
"ENABLE_MKTG_SITE": False,
"SITE_NAME": "openedx.localhost",
"course_org_filter": "CDX",
"course_about_show_social_links": False,
"css_overrides_file": "openedx/css/openedx.css",
"show_partners": False,
"show_homepage_promo_video": False,
"course_index_overlay_text": "Explore free courses from leading universities.",
"course_index_overlay_logo_file": "openedx/images/header-logo.png",
"homepage_overlay_html": "<h1>Take an Open edX Course</h1>"
},
}
if len(MICROSITE_CONFIGURATION.keys()) > 0:
enable_microsites(
MICROSITE_CONFIGURATION,
SUBDOMAIN_BRANDING,
VIRTUAL_UNIVERSITIES
)
......@@ -25,6 +25,7 @@ Longer TODO:
import sys
import os
import json
from path import path
......@@ -377,7 +378,7 @@ TRACKING_ENABLED = True
######################## subdomain specific settings ###########################
COURSE_LISTINGS = {}
SUBDOMAIN_BRANDING = {}
VIRTUAL_UNIVERSITIES = []
############################### XModule Store ##################################
MODULESTORE = {
......@@ -617,6 +618,7 @@ TEMPLATE_LOADERS = (
MIDDLEWARE_CLASSES = (
'request_cache.middleware.RequestCache',
'microsite_configuration.middleware.MicrositeConfiguration',
'django_comment_client.middleware.AjaxExceptionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
......@@ -1048,6 +1050,64 @@ MKTG_URL_LINK_MAP = {
}
############################### MICROSITES ################################
def enable_microsites(microsite_config_dict, subdomain_branding, virtual_universities, microsites_root=ENV_ROOT / "microsites"):
"""
Enable the use of microsites, which are websites that allow
for subdomains for the edX platform, e.g. foo.edx.org
"""
if not microsite_config_dict:
return
FEATURES['USE_MICROSITES'] = True
for microsite_name in microsite_config_dict.keys():
# Calculate the location of the microsite's files
microsite_root = microsites_root / microsite_name
microsite_config = microsite_config_dict[microsite_name]
# pull in configuration information from each
# microsite root
if os.path.isdir(microsite_root):
# store the path on disk for later use
microsite_config['microsite_root'] = microsite_root
# get the domain that this should reside
domain = microsite_config['domain_prefix']
# get the virtual university that this should use
university = microsite_config['university']
# add to the existing maps in our settings
subdomain_branding[domain] = university
virtual_universities.append(university)
template_dir = microsite_root / 'templates'
microsite_config['template_dir'] = template_dir
microsite_config['microsite_name'] = microsite_name
else:
# not sure if we have application logging at this stage of
# startup
print '**** Error loading microsite {0}. Directory does not exist'.format(microsite_root)
# remove from our configuration as it is not valid
del microsite_config_dict[microsite_name]
# if we have microsites, then let's turn on SUBDOMAIN_BRANDING
# Note check size of the dict because some microsites might not be found on disk and
# we could be left with none
if microsite_config_dict:
FEATURES['SUBDOMAIN_BRANDING'] = True
TEMPLATE_DIRS.append(microsites_root)
MAKO_TEMPLATES['main'].append(microsites_root)
STATICFILES_DIRS.append(microsites_root)
################# Student Verification #################
VERIFY_STUDENT = {
"DAYS_GOOD_FOR": 365, # How many days is a verficiation good for?
......
......@@ -130,6 +130,8 @@ SUBDOMAIN_BRANDING = {
'mit': 'MITx',
'berkeley': 'BerkeleyX',
'harvard': 'HarvardX',
'openedx': 'openedx',
'edge': 'edge',
}
# List of `university` landing pages to display, even though they may not
......
......@@ -277,3 +277,33 @@ PASSWORD_HASHERS = (
import openid.oidutil
openid.oidutil.log = lambda message, level = 0: None
# set up some testing for microsites
MICROSITE_CONFIGURATION = {
"test_microsite": {
"domain_prefix": "testmicrosite",
"university": "test_microsite",
"platform_name": "Test Microsite",
"logo_image_url": "test_microsite/images/header-logo.png",
"email_from_address": "test_microsite@edx.org",
"payment_support_email": "test_microsite@edx.org",
"ENABLE_MKTG_SITE": False,
"SITE_NAME": "test_microsite.localhost",
"course_org_filter": "TestMicrositeX",
"course_about_show_social_links": False,
"css_overrides_file": "test_microsite/css/test_microsite.css",
"show_partners": False,
"show_homepage_promo_video": False,
"course_index_overlay_text": "This is a Test Microsite Overlay Text.",
"course_index_overlay_logo_file": "test_microsite/images/header-logo.png",
"homepage_overlay_html": "<h1>This is a Test Microsite Overlay HTML</h1>"
}
}
if len(MICROSITE_CONFIGURATION.keys()) > 0:
enable_microsites(
MICROSITE_CONFIGURATION,
SUBDOMAIN_BRANDING,
VIRTUAL_UNIVERSITIES,
microsites_root=ENV_ROOT / 'edx-platform' / 'test_microsites'
)
......@@ -11,15 +11,20 @@
cart_link = ""
%>
<%namespace name='static' file='../static_content.html'/>
<%! from microsite_configuration.middleware import MicrositeConfiguration %>
<%inherit file="../main.html" />
<%block name="headextra">
% if self.theme_enabled():
<%include file="../theme-google-analytics.html" />
% else:
<%include file="../google_analytics.html" />
% endif
<%
if self.theme_enabled():
google_analytics_file = u'../' + MicrositeConfiguration.get_microsite_configuration_value('google_analytics_file', 'theme-google-analytics.html')
else:
google_analytics_file = '../google_analytics.html'
%>
<%include file="${google_analytics_file}" />
</%block>
<%block name="js_extra">
......@@ -196,6 +201,7 @@
<section class="course-sidebar">
<section class="course-summary">
<header>
% if MicrositeConfiguration.get_microsite_configuration_value('course_about_show_social_links', True):
<div class="social-sharing">
<div class="sharing-message">${_("Share with friends and family!")}</div>
## TODO: this should probably be an overrideable block,
......@@ -209,17 +215,28 @@
<img src="${static.url('images/social/email-sharing.png')}" alt="Email someone to say you've registered for this course">
</a>
% else:
<a href="http://twitter.com/intent/tweet?text=I+just+registered+for+${course.number}+${get_course_about_section(course, 'title')}+through+@edxonline:+http://www.edx.org${reverse('about_course', args=[course.id])}" class="share">
<%
site_domain = MicrositeConfiguration.get_microsite_configuration_value('site_domain', 'www.edx.org')
platform_name = MicrositeConfiguration.get_microsite_configuration_value('platform_name', 'edX')
tweet_action = "http://twitter.com/intent/tweet?text=I+just+registered+for+"+course.number+"+"+get_course_about_section(course, 'title')+"+through+"+MicrositeConfiguration.get_microsite_configuration_value('course_about_twitter_account', '@edxonline')+":+http://"+site_domain+reverse('about_course', args=[course.id])
facebook_link = MicrositeConfiguration.get_microsite_configuration_value('course_about_facebook_link', 'http://www.facebook.com/EdxOnline')
email_subject = "mailto:?subject=Take%20a%20course%20with%20"+platform_name+"%20online&body=I%20just%20registered%20for%20"+course.number+"%20"+get_course_about_section(course, 'title')+"%20through%20"+platform_name+"%20http://"+site_domain+reverse('about_course', args=[course.id])
%>
<a href="${tweet_action}" class="share">
<img src="${static.url('images/social/twitter-sharing.png')}" alt="Tweet that you've registered for this course">
</a>
<a href="http://www.facebook.com/EdxOnline" class="share">
<a href="${facebook_link}" class="share">
<img src="${static.url('images/social/facebook-sharing.png')}" alt="Post a Facebook message to say you've registered for this course">
</a>
<a href="mailto:?subject=Take%20a%20course%20with%20edX%20online&body=I%20just%20registered%20for%20${course.number}%20${get_course_about_section(course, 'title')}%20through%20edX:+http://edx.org/${reverse('about_course', args=[course.id])}" class="share">
<a href="${email_subject}" class="share">
<img src="${static.url('images/social/email-sharing.png')}" alt="Email someone to say you've registered for this course">
</a>
% endif
</div>
% endif
</header>
<ol class="important-dates">
......
......@@ -4,8 +4,20 @@
<%namespace name='static' file='../static_content.html'/>
<%block name="title"><title>${_("Courses")}</title></%block>
<%! from microsite_configuration.middleware import MicrositeConfiguration %>
<section class="find-courses">
<%
course_index_overlay_text = MicrositeConfiguration.get_microsite_configuration_value('course_index_overlay_text', _("Explore free courses from leading universities."))
# not sure why this is, but if I use static.url('images/edx_bw.png') then the HTML rendering
# of this template goes wonky
logo_file = MicrositeConfiguration.get_microsite_configuration_value(
'course_index_overlay_logo_file', settings.STATIC_URL + 'images/edx_bw.png')
%>
<header class="search">
<div class="inner-wrapper main-search">
<hgroup>
......@@ -13,13 +25,13 @@
% if self.stanford_theme_enabled():
<img src="${static.url('themes/stanford/images/seal.png')}" alt="Stanford Seal Logo" />
% else:
<img src="${static.url('images/edx_bw.png')}" alt="Black and White edX Logo" />
<img src='${logo_file}' alt="${MicrositeConfiguration.get_microsite_configuration_value('platform_name', settings.PLATFORM_NAME)} Logo" />
% endif
</div>
% if self.stanford_theme_enabled():
<h2>${_("Explore free courses from {university_name}.").format(university_name="Stanford University")}</h2>
% else:
<h2>${_("Explore free courses from leading universities.")}</h2>
<h2>${course_index_overlay_text}</h2>
% endif
</hgroup>
</div>
......
<%! from django.utils.translation import ugettext as _ %>
<%! from microsite_configuration.middleware import MicrositeConfiguration %>
<%! from django.core.urlresolvers import reverse %>
<section id="forgot-password-modal" class="modal" role="dialog" aria-label="${_('Password Reset')}">
......@@ -22,7 +23,7 @@
<li class="field required text" id="forgot-password-modal-field-email">
<label for="pwd_reset_email">${_("Your E-mail Address")}</label>
<input class="" id="pwd_reset_email" type="email" name="email" value="" placeholder="example: username@domain.com" aria-describedby="pwd_reset_email-tip" aria-required="true" />
<span class="tip tip-input" id="pwd_reset_email-tip">${_("This is the e-mail address you used to register with {platform}").format(platform=settings.PLATFORM_NAME)}</span>
<span class="tip tip-input" id="pwd_reset_email-tip">${_("This is the e-mail address you used to register with {platform}").format(platform=MicrositeConfiguration.get_microsite_configuration_value('platform_name', settings.PLATFORM_NAME))}</span>
</li>
</ol>
</fieldset>
......
......@@ -5,6 +5,7 @@
<%! import pytz %>
<%! from django.conf import settings %>
<%! from courseware.tabs import get_discussion_link %>
<%! from microsite_configuration.middleware import MicrositeConfiguration %>
% if settings.FEATURES.get('ENABLE_FEEDBACK_SUBMISSION', False):
......@@ -12,13 +13,13 @@
<a href="#help-modal" rel="leanModal" role="button">${_("Help")}</a>
</div>
<section id="help-modal" class="modal" aria-hidden="true" role="dialog" aria-label='${_("{platform_name} Help").format(platform_name=settings.PLATFORM_NAME)}'>
<section id="help-modal" class="modal" aria-hidden="true" role="dialog" aria-label="${_("{platform_name} Help").format(platform_name=MicrositeConfiguration.get_microsite_configuration_value("platform_name", settings.PLATFORM_NAME))}">
<div class="inner-wrapper" id="help_wrapper">
## TODO: find a way to refactor this
<button class="close-modal "tabindex="0">&#10005; <span class="sr">${_('Close Modal')}</span></button>
<header>
<h2>${_('{span_start}{platform_name}{span_end} Help').format(span_start='<span class="edx">', span_end='</span>', platform_name=settings.PLATFORM_NAME)}</h2>
<h2>${_('{span_start}{platform_name}{span_end} Help').format(span_start='<span class="edx">', span_end='</span>', platform_name=MicrositeConfiguration.get_microsite_configuration_value('platform_name', settings.PLATFORM_NAME))}</h2>
<hr>
</header>
......@@ -39,10 +40,10 @@ discussion_link = get_discussion_link(course) if course else None
url=marketing_link('FAQ')
),
link_end='</a>',
platform_name=settings.PLATFORM_NAME)}
platform_name=MicrositeConfiguration.get_microsite_configuration_value('platform_name', settings.PLATFORM_NAME))}
</p>
<p>${_('Have a <strong>question about something specific</strong>? You can contact the {platform_name} general support team directly:').format(platform_name=settings.PLATFORM_NAME)}</p>
<p>${_('Have a <strong>question about something specific</strong>? You can contact the {platform_name} general support team directly:').format(platform_name=MicrositeConfiguration.get_microsite_configuration_value('platform_name', settings.PLATFORM_NAME))}</p>
<hr>
<div class="help-buttons">
......
......@@ -5,17 +5,33 @@
<%inherit file="main.html" />
<%namespace name='static' file='static_content.html'/>
<%! from microsite_configuration.middleware import MicrositeConfiguration %>
<%
homepage_overlay_html = MicrositeConfiguration.get_microsite_configuration_value('homepage_overlay_html')
show_homepage_promo_video = MicrositeConfiguration.get_microsite_configuration_value('show_homepage_promo_video', True)
homepage_promo_video_youtube_id = MicrositeConfiguration.get_microsite_configuration_value('homepage_promo_video_youtube_id', "XNaiOGxWeto")
show_partners = MicrositeConfiguration.get_microsite_configuration_value('show_partners', True)
%>
<section class="home">
<header>
<div class="outer-wrapper">
<div class="title">
<hgroup>
% if self.stanford_theme_enabled():
<h1>${_("Free courses from <strong>{university_name}</strong>").format(university_name="Stanford")}</h1>
% if homepage_overlay_html:
${homepage_overlay_html}
% else:
<h1>${_("The Future of Online Education")}</h1>
% if self.stanford_theme_enabled():
<h1>${_("Free courses from <strong>{university_name}</strong>").format(university_name="Stanford")}</h1>
% else:
<h1>${_("The Future of Online Education")}</h1>
% endif
<h2>${_("For anyone, anywhere, anytime")}</h2>
% endif
<h2>${_("For anyone, anywhere, anytime")}</h2>
</hgroup>
## Disable social buttons for non-edX sites
......@@ -40,22 +56,22 @@
</div>
</div>
</section>
% endif
% endif
</div>
% if show_homepage_promo_video:
<a href="#video-modal" class="media" rel="leanModal">
<div class="hero">
<div class="play-intro"></div>
</div>
</a>
% endif
</div>
</header>
<section class="container">
<section class="highlighted-courses">
## Disable university partner logos and sites for non-edX sites
% if not self.theme_enabled():
% if not self.theme_enabled() and show_partners:
<h2>${_('Explore free courses from {span_start}{platform_name}{span_end} universities').format(platform_name="edX", span_start='<span class="edx">', span_end='</span>')}</h2>
<section class="university-partners university-partners2x6">
......@@ -183,10 +199,9 @@
<section id="video-modal" class="modal home-page-video-modal video-modal">
<div class="inner-wrapper">
<%
youtube_video_id = homepage_promo_video_youtube_id
if self.stanford_theme_enabled():
youtube_video_id = "2gmreZObCY4"
else:
youtube_video_id = "XNaiOGxWeto"
%>
<iframe width="640" height="360" src="//www.youtube.com/embed/${youtube_video_id}?showinfo=0" frameborder="0" allowfullscreen></iframe>
</div>
......
<%!
from django.utils.translation import ugettext as _
from django.core.urlresolvers import reverse
%>
<header>
<h2 class="sr">${_("Helpful Information")}</h2>
</header>
% if settings.FEATURES.get('AUTH_USE_OPENID'):
<!-- <div class="cta cta-login-options-openid">
<h3>${_("Login via OpenID")}</h3>
<p>${_('You can now start learning with {platform_name} by logging in with your <a rel="external" href="http://openid.net/">OpenID account</a>.').format(platform_name=platform_name)}</p>
<a class="action action-login-openid" href="#">${_("Login via OpenID")}</a>
</div> -->
% endif
<div class="cta cta-help">
<h3>${_("Not Enrolled?")}</h3>
<p><a href="${reverse('register_user')}">${_("Sign up for {platform_name} today!").format(platform_name=platform_name)}</a></p>
## Disable help unless the FAQ marketing link is enabled
% if settings.MKTG_URL_LINK_MAP.get('FAQ'):
<h3>${_("Need Help?")}</h3>
<p>${_("Looking for help in logging in or with your {platform_name} account?").format(platform_name=platform_name)}
<a href="${marketing_link('FAQ')}">
${_("View our help section for answers to commonly asked questions.")}
</a></p>
% endif
</div>
\ No newline at end of file
<%inherit file="main.html" />
<%! from microsite_configuration.middleware import MicrositeConfiguration %>
<%namespace name='static' file='static_content.html'/>
<%! from django.core.urlresolvers import reverse %>
<%! from django.utils.translation import ugettext as _ %>
<%block name="title"><title>${_("Log into your {platform_name} Account").format(platform_name=settings.PLATFORM_NAME)}</title></%block>
<%block name="title"><title>${_("Log into your {platform_name} Account").format(platform_name=platform_name)}</title></%block>
<%block name="js_extra">
<script type="text/javascript">
......@@ -83,7 +83,7 @@
$submitButton.
removeClass('is-disabled').
removeProp('disabled').
html("${_('Log into My {platform_name} Account').format(platform_name=settings.PLATFORM_NAME)} <span class='orn-plus'>+</span> ${_('Access My Courses')}");
html("${_('Log into My {platform_name} Account').format(platform_name=platform_name)} <span class='orn-plus'>+</span> ${_('Access My Courses')}");
}
else {
$submitButton.
......@@ -110,7 +110,7 @@
<!-- status messages -->
<div role="alert" class="status message">
<h3 class="message-title">${_("We're Sorry, {platform_name} accounts are unavailable currently").format(platform_name=settings.PLATFORM_NAME)}</h3>
<h3 class="message-title">${_("We're Sorry, {platform_name} accounts are unavailable currently").format(platform_name=platform_name)}</h3>
</div>
<div role="alert" class="status message submission-error" tabindex="-1">
......@@ -121,7 +121,7 @@
</div>
<p class="instructions sr">
${_('Please provide the following information to log into your {platform_name} account. Required fields are noted by <strong class="indicator">bold text and an asterisk (*)</strong>.').format(platform_name=settings.PLATFORM_NAME)}
${_('Please provide the following information to log into your {platform_name} account. Required fields are noted by <strong class="indicator">bold text and an asterisk (*)</strong>.').format(platform_name=platform_name)}
</p>
<div class="group group-form group-form-requiredinformation">
......@@ -131,7 +131,7 @@
<li class="field required text" id="field-email">
<label for="email">${_('E-mail')}</label>
<input class="" id="email" type="email" name="email" value="" placeholder="example: username@domain.com" required aria-required="true" aria-described-by="email-tip" />
<span class="tip tip-input" id="email-tip">${_("This is the e-mail address you used to register with {platform}").format(platform=settings.PLATFORM_NAME)}</span>
<span class="tip tip-input" id="email-tip">${_("This is the e-mail address you used to register with {platform}").format(platform=platform_name)}</span>
</li>
<li class="field required password" id="field-password">
<label for="password">${_('Password')}</label>
......@@ -166,31 +166,14 @@
</section>
<aside role="complementary">
<header>
<h2 class="sr">${_("Helpful Information")}</h2>
</header>
% if settings.FEATURES.get('AUTH_USE_OPENID'):
<!-- <div class="cta cta-login-options-openid">
<h3>${_("Login via OpenID")}</h3>
<p>${_('You can now start learning with {platform_name} by logging in with your <a rel="external" href="http://openid.net/">OpenID account</a>.').format(platform_name=settings.PLATFORM_NAME)}</p>
<a class="action action-login-openid" href="#">${_("Login via OpenID")}</a>
</div> -->
% endif
<div class="cta cta-help">
<h3>${_("Not Enrolled?")}</h3>
<p><a href="${reverse('register_user')}">${_("Sign up for {platform_name} today!").format(platform_name=settings.PLATFORM_NAME)}</a></p>
## Disable help unless the FAQ marketing link is enabled
% if settings.MKTG_URL_LINK_MAP.get('FAQ'):
<h3>${_("Need Help?")}</h3>
<p>${_("Looking for help in logging in or with your {platform_name} account?").format(platform_name=settings.PLATFORM_NAME)}
<a href="${marketing_link('FAQ')}">
${_("View our help section for answers to commonly asked questions.")}
</a></p>
% endif
</div>
<%
# allow for microsite overrides on the registration sidebars, otherwise default to pre-existing ones
sidebar_file = MicrositeConfiguration.get_microsite_template_path('login-sidebar.html')
%>
<%include file="${sidebar_file}" />
</aside>
</section>
<%! from django.utils.translation import ugettext as _ %>
<%! from microsite_configuration.middleware import MicrositeConfiguration %>
<%namespace name='static' file='static_content.html'/>
<%! from django.utils import html %>
......@@ -12,7 +13,12 @@
</%def>
<%def name="stanford_theme_enabled()">
<% return theme_enabled() and getattr(settings, "THEME_NAME") == "stanford" %>
<%
if not theme_enabled():
return False
return getattr(settings, "THEME_NAME", None) == "stanford"
%>
</%def>
<!DOCTYPE html>
......@@ -26,10 +32,9 @@
<title>${_("Home")} | class.stanford.edu</title>
% else:
## "edX" should not be translated
<title>edX</title>
<title>${MicrositeConfiguration.get_microsite_configuration_value('platform_name', settings.PLATFORM_NAME)}</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script type="text/javascript">
/* immediately break out of an iframe if coming from the marketing website */
......@@ -44,7 +49,7 @@
<script type="text/javascript" src="/jsi18n/"></script>
<link rel="icon" type="image/x-icon" href="${static.url(settings.FAVICON_PATH)}" />
<link rel="icon" type="image/x-icon" href="${static.url(MicrositeConfiguration.get_microsite_configuration_value('favicon_path', settings.FAVICON_PATH))}" />
<%static:css group='style-vendor'/>
<%static:css group='style-app'/>
......@@ -54,8 +59,27 @@
<%static:js group='main_vendor'/>
<%block name="headextra"/>
% if theme_enabled():
<%include file="theme-head-extra.html" />
<%
if theme_enabled():
header_extra_file = 'theme-head-extra.html'
header_file = 'theme-header.html'
google_analytics_file = 'theme-google-analytics.html'
footer_file = 'theme-footer.html'
style_overrides_file = None
else:
header_extra_file = None
header_file = MicrositeConfiguration.get_microsite_template_path('navigation.html')
google_analytics_file = MicrositeConfiguration.get_microsite_template_path('google_analytics.html')
footer_file = MicrositeConfiguration.get_microsite_template_path('footer.html')
style_overrides_file = MicrositeConfiguration.get_microsite_configuration_value('css_overrides_file')
%>
% if header_extra_file:
<%include file="${header_extra_file}" />
% endif
<!--[if lt IE 9]>
......@@ -66,15 +90,16 @@
<meta name="google-site-verification" content="_mipQ4AtZQDNmbtOkwehQDOgCxUUV2fb_C0b6wbiRHY" />
% if not course:
% if theme_enabled():
<%include file="theme-google-analytics.html" />
% else:
<%include file="google_analytics.html" />
% endif
<%include file="${google_analytics_file}" />
% endif
<%include file="widgets/segment-io.html" />
% if style_overrides_file:
<link rel="stylesheet" type="text/css" href="${static.url(style_overrides_file)}" />
% endif
</head>
<body class="<%block name='bodyclass'/>">
......@@ -82,21 +107,17 @@
<%include file="mathjax_accessible.html" />
% if theme_enabled():
<%include file="theme-header.html" />
% elif not suppress_toplevel_navigation:
<%include file="navigation.html" />
% endif
% if not suppress_toplevel_navigation:
<%include file="${header_file}" />
%endif
<section class="content-wrapper" id="content">
${self.body()}
<%block name="bodyextra"/>
</section>
% if theme_enabled():
<%include file="theme-footer.html" />
% elif not suppress_toplevel_navigation:
<%include file="footer.html" />
% if not suppress_toplevel_navigation:
<%include file="${footer_file}" />
% endif
<script>window.baseUrl = "${settings.STATIC_URL}";</script>
......@@ -113,3 +134,5 @@
html.escape(enrollment_action)
) if course_id and enrollment_action else ""
}</%def>
......@@ -11,6 +11,8 @@ import branding
from status.status import get_site_status_msg
%>
<%! from microsite_configuration.middleware import MicrositeConfiguration %>
## Provide a hook for themes to inject branding on top.
<%block name="navigation_top" />
......@@ -44,7 +46,7 @@ site_status_msg = get_site_status_msg(course_id)
<h1 class="logo">
<a href="${marketing_link('ROOT')}">
<%block name="navigation_logo">
<img src="${static.url(branding.get_logo_url(request.META.get('HTTP_HOST')))}" alt="${settings.PLATFORM_NAME} ${_('Home')}" />
<img src="${static.url(branding.get_logo_url())}" alt="${MicrositeConfiguration.get_microsite_configuration_value('platform_name', settings.PLATFORM_NAME)} ${_('Home')}" />
</%block>
</a>
</h1>
......
<%!
from django.utils.translation import ugettext as _
from django.core.urlresolvers import reverse
%>
<%namespace file='main.html' import="login_query, stanford_theme_enabled"/>
<%namespace name='static' file='static_content.html'/>
<header>
<h3 class="sr">${_("Registration Help")}</h3>
</header>
% if has_extauth_info is UNDEFINED:
<div class="cta">
<h3>${_("Already registered?")}</h3>
<p class="instructions">
<a href="${reverse('signin_user')}${login_query()}">
${_("Click here to log in.")}
</a>
</p>
</div>
% endif
## TODO: Use a %block tag or something to allow themes to
## override in a more generalizable fashion.
% if not stanford_theme_enabled():
<div class="cta cta-welcome">
<h3>${_("Welcome to {platform_name}").format(platform_name=platform_name)}</h3>
<p>${_("Registering with {platform_name} gives you access to all of our current and future free courses. Not ready to take a course just yet? Registering puts you on our mailing list - we will update you as courses are added.").format(platform_name=platform_name)}</p>
</div>
% endif
<div class="cta cta-nextsteps">
<h3>${_("Next Steps")}</h3>
% if stanford_theme_enabled():
<p>${_("You will receive an activation email. You must click on the activation link to complete the process. Don't see the email? Check your spam folder and mark emails from class.stanford.edu as 'not spam', since you'll want to be able to receive email from your courses.")}</p>
% else:
<p>${_("As part of joining {platform_name}, you will receive an activation email. You must click on the activation link to complete the process. Don't see the email? Check your spam folder and mark {platform_name} emails as 'not spam'. At {platform_name}, we communicate mostly through email.").format(platform_name=platform_name)}</p>
% endif
</div>
% if settings.MKTG_URL_LINK_MAP.get('FAQ'):
<div class="cta cta-help">
<h3>${_("Need Help?")}</h3>
<p>${_("Need help in registering with {platform_name}?").format(platform_name=platform_name)}
<a href="${marketing_link('FAQ')}">
${_("View our FAQs for answers to commonly asked questions.")}
</a>
${_("Once registered, most questions can be answered in the course specific discussion forums or through the FAQs.")}</p>
</div>
% endif
<%! from django.utils.translation import ugettext as _ %>
<%! from microsite_configuration.middleware import MicrositeConfiguration %>
<%inherit file="main.html" />
......@@ -13,7 +14,7 @@
<%! from datetime import date %>
<%! import calendar %>
<%block name="title"><title>${_("Register for {platform_name}").format(platform_name=settings.PLATFORM_NAME)}</title></%block>
<%block name="title"><title>${_("Register for {platform_name}").format(platform_name=platform_name)}</title></%block>
<%block name="js_extra">
<script type="text/javascript">
......@@ -76,7 +77,7 @@
$submitButton.
removeClass('is-disabled').
removeProp('disabled').
text("${_('Create My {platform_name} Account').format(platform_name=settings.PLATFORM_NAME)}");
html("${_('Create My {platform_name} Account').format(platform_name=platform_name)}");
}
else {
$submitButton.
......@@ -92,7 +93,7 @@
<header>
<h1 class="title">
<span class="title-super">${_("Welcome!")}</span>
<span class="title-sub">${_("Register below to create your {platform_name} account").format(platform_name=settings.PLATFORM_NAME)}</span>
<span class="title-sub">${_("Register below to create your {platform_name} account").format(platform_name=platform_name)}</span>
</h1>
</header>
</section>
......@@ -104,7 +105,7 @@
<!-- status messages -->
<div role="alert" class="status message">
<h3 class="message-title">${_("We're sorry, {platform_name} enrollment is not available in your region").format(platform_name=settings.PLATFORM_NAME)}</h3>
<h3 class="message-title">${_("We're sorry, {platform_name} enrollment is not available in your region").format(platform_name=platform_name)}</h3>
</div>
<div role="alert" class="status message submission-error" tabindex="-1">
......@@ -241,7 +242,7 @@
% if 'goals' in settings.REGISTRATION_OPTIONAL_FIELDS:
<li class="field text" id="field-goals">
<label for="goals">${_("Please share with us your reasons for registering with {platform_name}").format(platform_name=settings.PLATFORM_NAME)}</label>
<label for="goals">${_("Please share with us your reasons for registering with {platform_name}").format(platform_name=platform_name)}</label>
<textarea id="goals" name="goals" value=""></textarea>
</li>
% endif
......@@ -294,50 +295,13 @@
</section>
<aside role="complementary">
<header>
<h3 class="sr">${_("Registration Help")}</h3>
</header>
% if has_extauth_info is UNDEFINED:
<%
# allow for microsite overrides on the registration sidebars, otherwise default to pre-existing ones
sidebar_file = MicrositeConfiguration.get_microsite_template_path('register-sidebar.html')
%>
<div class="cta">
<h3>${_("Already registered?")}</h3>
<p class="instructions">
<a href="${reverse('signin_user')}${login_query()}">
${_("Click here to log in.")}
</a>
</p>
</div>
% endif
<%include file="${sidebar_file}" />
## TODO: Use a %block tag or something to allow themes to
## override in a more generalizable fashion.
% if not self.stanford_theme_enabled():
<div class="cta cta-welcome">
<h3>${_("Welcome to {platform_name}").format(platform_name=settings.PLATFORM_NAME)}</h3>
<p>${_("Registering with {platform_name} gives you access to all of our current and future free courses. Not ready to take a course just yet? Registering puts you on our mailing list - we will update you as courses are added.").format(platform_name=settings.PLATFORM_NAME)}</p>
</div>
% endif
<div class="cta cta-nextsteps">
<h3>${_("Next Steps")}</h3>
% if self.stanford_theme_enabled():
<p>${_("You will receive an activation email. You must click on the activation link to complete the process. Don't see the email? Check your spam folder and mark emails from class.stanford.edu as 'not spam', since you'll want to be able to receive email from your courses.")}</p>
% else:
<p>${_("As part of joining {platform_name}, you will receive an activation email. You must click on the activation link to complete the process. Don't see the email? Check your spam folder and mark {platform_name} emails as 'not spam'. At {platform_name}, we communicate mostly through email.").format(platform_name=settings.PLATFORM_NAME)}</p>
% endif
</div>
% if settings.MKTG_URL_LINK_MAP.get('FAQ'):
<div class="cta cta-help">
<h3>${_("Need Help?")}</h3>
<p>${_("Need help in registering with {platform_name}?").format(platform_name=settings.PLATFORM_NAME)}
<a href="${marketing_link('FAQ')}">
${_("View our FAQs for answers to commonly asked questions.")}
</a>
${_("Once registered, most questions can be answered in the course specific discussion forums or through the FAQs.")}</p>
</div>
% endif
</aside>
</section>
.find-courses header.search, .university-profile header.search {
background-image: url("../images/background-image.jpg");
}
.course-info header.course-profile {
background: url("../images/background-image.jpg") repeat scroll 0 0 / cover #F5F5F5;
}
.view-login .introduction header {
background-image: url("../images/login-and-register-banner.png");
}
.view-register .introduction header {
background-image: url("../images/login-and-register-banner.png");
}
\ No newline at end of file
<%! from django.utils.translation import ugettext as _ %>
${_("Thank you for signing up for Open edX! To activate "
"your account, please copy and paste this address into your web "
"browser's address bar:")}
% if is_secure:
https://${ site }/activate/${ key }
% else:
http://${ site }/activate/${ key }
% endif
${_("If you didn't request this, you don't need to do anything; you won't "
"receive any more email from us. Please do not reply to this e-mail; "
"if you require assistance, check the help section of the "
"Open edX web site.")}
<%! from django.utils.translation import ugettext as _ %>
${_("Your account for Open edX")}
<%! from django.core.urlresolvers import reverse %>
This is to confirm that you changed the e-mail associated with
Open edX from ${old_email} to ${new_email}. If you
did not make this request, please contact us immediately. Contact
information is listed at:
% if is_secure:
https://${ site }${reverse('contact')}
% else:
http://${ site }${reverse('contact')}
% endif
We keep a log of old e-mails, so if this request was unintentional, we
can investigate.
We received a request to change the e-mail associated with your
Open edX account from ${old_email} to ${new_email}.
If this is correct, please confirm your new e-mail address by
visiting:
% if is_secure:
https://${ site }/email_confirm/${ key }
% else:
http://${ site }/email_confirm/${ key }
% endif
If you didn't request this, you don't need to do anything; you won't
receive any more email from us. Please do not reply to this e-mail;
if you require assistance, check the help section of the
Open edX web site.
<%! from django.utils.translation import ugettext as _ %>
${_("Dear student,")}
${_("You have been invited to join {course_name} at {site_name} by a "
"member of the course staff.").format(
course_name=course.display_name_with_default,
site_name=site_name
)}
${_("To finish your registration, please visit {registration_url} and fill "
"out the registration form making sure to use {email_address} in the E-mail field.").format(
registration_url=registration_url,
email_address=email_address
)}
% if auto_enroll:
${_("Once you have registered and activated your account, you will see "
"{course_name} listed on your dashboard.").format(
course_name=course.display_name_with_default
)}
% else:
${_("Once you have registered and activated your account, visit {course_about_url} "
"to join the course.").format(course_about_url=course_about_url)}
% endif
----
${_("This email was automatically sent from {site_name} to "
"{email_address}").format(
site_name=site_name, email_address=email_address
)}
\ No newline at end of file
<%! from django.utils.translation import ugettext as _ %>
${_("You have been invited to register for {course_name}").format(
course_name=course.display_name_with_default
)}
\ No newline at end of file
<%! from django.utils.translation import ugettext as _ %>
${_("Dear {full_name}").format(full_name=full_name)}
${_("You have been enrolled in {course_name} at {site_name} by a member "
"of the course staff. The course should now appear on your {site_name} "
"dashboard.").format(
course_name=course.display_name_with_default,
site_name=site_name
)}
${_("To start accessing course materials, please visit {course_url}").format(
course_url=course_url
)}
----
${_("This email was automatically sent from {site_name} to "
"{full_name}").format(
site_name=site_name, full_name=full_name
)}
\ No newline at end of file
<%! from django.utils.translation import ugettext as _ %>
${_("You have been enrolled in {course_name}").format(
course_name=course.display_name_with_default
)}
\ No newline at end of file
<%! from django.utils.translation import ugettext as _ %>
${_("Dear Student,")}
${_("You have been un-enrolled from course {course_name} by a member "
"of the course staff. Please disregard the invitation "
"previously sent.").format(course_name=course.display_name_with_default)}
----
${_("This email was automatically sent from {site_name} "
"to {email_address}").format(
site_name=site_name, email_address=email_address
)}
\ No newline at end of file
<%! from django.utils.translation import ugettext as _ %>
${_("Dear {full_name}").format(full_name=full_name)}
${_("You have been un-enrolled in {course_name} at {site_name} by a member "
"of the course staff. The course will no longer appear on your "
"{site_name} dashboard.").format(
course_name=course.display_name_with_default, site_name=site_name
)}
${_("Your other courses have not been affected.")}
----
${_("This email was automatically sent from {site_name} to "
"{full_name}").format(
full_name=full_name, site_name=site_name
)}
\ No newline at end of file
<%! from django.utils.translation import ugettext as _ %>
${_("You have been un-enrolled from {course_name}").format(
course_name=course.display_name_with_default
)}
\ No newline at end of file
## mako
<%! from django.core.urlresolvers import reverse %>
<%! from django.utils.translation import ugettext as _ %>
<%! from microsite_configuration.middleware import MicrositeConfiguration %>
<%namespace name='static' file='static_content.html'/>
<div class="wrapper wrapper-footer">
<footer>
<div class="colophon">
<div class="colophon-about">
<p>This is a Test Microsite footer</p>
</div>
</div>
</footer>
</div>
<%!
from django.utils.translation import ugettext as _
from django.core.urlresolvers import reverse
%>
<header>
<h2 class="sr">${_("Helpful Information")}</h2>
</header>
<div class="cta cta-help">
<h3>${_("Not Enrolled?")}</h3>
<p><a href="${reverse('register_user')}">${_("Sign up for {platform_name} today!").format(platform_name=platform_name)}</a></p>
<h3>${_("Need Help?")}</h3>
<p>
Custom text
</p>
</div>
\ No newline at end of file
<%!
from django.utils.translation import ugettext as _
from django.core.urlresolvers import reverse
%>
<%namespace file='../../main.html' import="login_query"/>
<%namespace name='static' file='../../static_content.html'/>
<header>
<h3 class="sr">${_("Registration Help")}</h3>
</header>
<div class="cta">
<h3>${_("Already registered?")}</h3>
<p class="instructions">
<a href="${reverse('signin_user')}${login_query()}">
${_("Click here to log in.")}
</a>
</p>
</div>
<div class="cta cta-welcome">
<h3>${_("Welcome to {platform_name}").format(platform_name=platform_name)}</h3>
<p>${_("Registering with {platform_name} gives you access to all of our current and future free courses. Not ready to take a course just yet? Registering puts you on our mailing list - we will update you as courses are added.").format(platform_name=platform_name)}</p>
</div>
<div class="cta cta-nextsteps">
<h3>${_("Next Steps")}</h3>
<p>${_("As part of joining {platform_name}, you will receive an activation email. You must click on the activation link to complete the process. Don't see the email? Check your spam folder and mark {platform_name} emails as 'not spam'. At {platform_name}, we communicate mostly through email.").format(platform_name=platform_name)}</p>
</div>
<div class="cta cta-help">
<h3>${_("Need Help?")}</h3>
<p>
This is custom area
</p>
</div>
<%! from django.utils.translation import ugettext as _ %>
<%! from django.core.urlresolvers import reverse %>
<%namespace name='static' file='../../../static_content.html'/>
<%inherit file="../../../main.html" />
<%block name="title"><title>${_("About {edX}").format(edX="edX")}</title></%block>
<section class="container about">
<nav>
<a href="${reverse('about_edx')}" class="active">${_("Vision")}</a>
<a href="${reverse('faq_edx')}">${_("Faq")}</a>
<a href="${reverse('press')}">${_("Press")}</a>
<a href="${reverse('contact')}">${_("Contact")}</a>
</nav>
<section class="vision">
## <div class="our-mission">
## <div class="logo">
## <img src="${static.url('images/edx-logo-large-bw.png')}">
## </div>
## <h2 class="mission-quote">&ldquo;${_('The mission of <span class="edx">{edX}</span> is to enhance human fulfillment worldwide through online ## learning, transforming education in quality, efficiency and scale through technology and research, for the benefit of campus-based ## students and the worldwide community of online learners.').format(edX="edX")}&rdquo;</h2>
## </div>
<section class="message left">
<div class="photo">
<img src="${static.url('images/about_1.jpg')}">
</div>
<article>
<h2>${_('About {span_start}{edX}{span_end}'.format(span_start='<span class="edx">', span_end='</span>', edX="edX"))}</h2>
<p>${_("Open edX is a not-for-profit enterprise of its founding partners {harvard_university} and the {massachusetts_institute_of_technology} that features learning designed specifically for interactive study via the web. Based on a long history of collaboration and their shared educational missions, the founders are creating a new online-learning experience with online courses that reflect their disciplinary breadth. Along with offering online courses, the institutions will use {edX} to research how students learn and how technology can transform learning&ndash;both on-campus and worldwide. Anant Agarwal, former Director of {MIT}'s Computer Science and Artificial Intelligence Laboratory, serves as the first president of {edX}. {EdX}'s goals combine the desire to reach out to students of all ages, means, and nations, and to deliver these teachings from a faculty who reflect the diversity of its audience. {EdX} is based in Cambridge, Massachusetts and is governed by {MIT} and {Harvard}.").format(edX="edX", EdX="EdX", harvard_university="Harvard University", Harvard="Harvard", MIT="MIT", massachusetts_institute_of_technology="Massachusetts Institute of Technology")}</p>
</article>
<hr class="fade-right-hr-divider">
</section>
<section class="message left">
<div class="photo">
<img src="${static.url('images/university/harvard/about-harvard.jpg')}">
</div>
<article>
<h2>${_("{harvard_university}").format(harvard_university="Harvard University")}</h2>
<p>${_("{harvard_university} is devoted to excellence in teaching, learning, and research, and to developing leaders in many disciplines who make a difference globally. {Harvard} faculty are engaged with teaching and research to push the boundaries of human knowledge. The University has twelve degree-granting Schools in addition to the {radcliffe_institute_for_advanced_study}.").format(harvard_university="Harvard University", Harvard="Harvard", radcliffe_institute_for_advanced_study="Radcliffe Institute for Advanced Study")}</p>
<p>${_("Established in 1636, {Harvard} is the oldest institution of higher education in the United States. The University, which is based in Cambridge and Boston, Massachusetts, has an enrollment of over 20,000 degree candidates, including undergraduate, graduate, and professional students. {Harvard} has more than 360,000 alumni around the world.").format(Harvard="Harvard")}</p>
</article>
<hr class="fade-left-hr-divider">
</section>
<section class="message left">
<div class="photo">
<img src="${static.url('images/university/mit/about-mit.jpg')}">
</div>
<article>
<h2>${_("{massachusetts_institute_of_technology}").format(massachusetts_institute_of_technology="Massachusetts Institute of Technology")}</h2>
<p>${_("The {massachusetts_institute_of_technology} &mdash; a coeducational, privately endowed research university founded in 1861 &mdash; is dedicated to advancing knowledge and educating students in science, technology, and other areas of scholarship that will best serve the nation and the world in the 21st century. The Institute has close to 1,000 faculty and 10,000 undergraduate and graduate students. It is organized into five Schools: Architecture and Urban Planning; Engineering; Humanities, Arts, and Social Sciences; {Sloan} School of Management; and Science.").format(massachusetts_institute_of_technology="Massachusetts Institute of Technology", Sloan="Sloan")}</p>
<p>${_("{MIT}'s commitment to innovation has led to a host of scientific breakthroughs and technological advances. Achievements of the Institute's faculty and graduates have included the first chemical synthesis of penicillin and vitamin A, the development of inertial guidance systems, modern technologies for artificial limbs, and the magnetic core memory that made possible the development of digital computers. 78 alumni, faculty, researchers and staff have won Nobel Prizes.").format(MIT="MIT")}</p>
<p>${_("Current areas of research and education include neuroscience and the study of the brain and mind, bioengineering, cancer, energy, the environment and sustainable development, information sciences and technology, new media, financial technology, and entrepreneurship.")}</p>
</article>
</section>
<section class="partners">
</section>
</section>
</section>
<%! from django.utils.translation import ugettext as _ %>
<%! from django.core.urlresolvers import reverse %>
<%namespace name='static' file='../static_content.html'/>
<%inherit file="../main.html" />
<%block name="title"><title>${_("Contact {platform_name}").format(platform_name=settings.PLATFORM_NAME)}</title></%block>
<section class="container about">
<nav>
<a href="${reverse('about_edx')}">${_("Vision")}</a>
<a href="${reverse('faq_edx')}">${_("Faq")}</a>
<a href="${reverse('press')}">${_("Press")}</a>
<a href="${reverse('contact')}" class="active">${_("Contact")}</a>
</nav>
<section class="contact">
<div class="photo">
<img src="${static.url('images/contact-page.jpg')}">
</div>
<div class="contacts">
<h2>${_("Class Feedback")}</h2>
<p>${_("We are always seeking feedback to improve our courses. If you are an enrolled student and have any questions, feedback, suggestions, or any other issues specific to a particular class, please post on the discussion forums of that&nbsp;class.")}</p>
<h2>${_("General Inquiries and Feedback")}</h2>
<p>${_('If you have a general question about {platform_name} please email {email}. To see if your question has already been answered, visit our {faq_link_start}FAQ page{faq_link_end}. You can also join the discussion on our {fb_link_start}facebook page{fb_link_end}. Though we may not have a chance to respond to every email, we take all feedback into consideration.').format(
platform_name=settings.PLATFORM_NAME,
email='<a href="mailto:{contact_email}">{contact_email}</a>'.format(contact_email=settings.CONTACT_EMAIL),
faq_link_start='<a href="{url}">'.format(url=reverse('faq_edx')),
faq_link_end='</a>',
fb_link_start='<a href="http://www.facebook.com/EdxOnline">',
fb_link_end='</a>'
)}</p>
<h2>${_("Technical Inquiries and Feedback")}</h2>
<p>${_('If you have suggestions/feedback about the overall {platform_name} platform, or are facing general technical issues with the platform (e.g., issues with email addresses and passwords), you can reach us at {tech_email}. For technical questions, please make sure you are using a current version of Firefox or Chrome, and include browser and version in your e-mail, as well as screenshots or other pertinent details. If you find a bug or other issues, you can reach us at the following: {bug_email}.').format(
platform_name=settings.PLATFORM_NAME,
tech_email='<a href="mailto:{tech_support_email}">{tech_support_email}</a>'.format(tech_support_email=settings.TECH_SUPPORT_EMAIL),
bug_email='<a href="mailto:{bugs_email}">{bugs_email}</a>'.format(bugs_email=settings.BUGS_EMAIL)
)}</p>
<h2>${_("Media")}</h2>
<p>${_('Please visit our {link_start}media/press page{link_end} for more information. For any media or press inquiries, please email {email}.').format(
link_start='<a href="{url}">'.format(url=reverse('faq_edx')),
link_end='</a>',
email='<a href="mailto:{email}">{email}</a>'.format(email="press@edx.org"),
)}</p>
<h2>${_("Universities")}</h2>
<p>${_('If you are a university wishing to collaborate or you have questions about {platform_name}, please email {email}.'.format(
platform_name="edX",
email='<a href="mailto:{email}">{email}</a>'.format(
email="university@edx.org"
)
))}</p>
<h2>${_("Accessibility")}</h2>
<p>${_('{platform_name} strives to create an innovative online-learning platform that promotes accessibility for everyone, including students with disabilities. We are dedicated to improving the accessibility of the platform and welcome your comments or questions at {email}.'.format(platform_name="EdX", email='<a href="mailto:{email}">{email}</a>'.format(email="accessibility@edx.org")))}</p>
</div>
</section>
</section>
<%! from django.utils.translation import ugettext as _ %>
<%! from django.core.urlresolvers import reverse %>
<%namespace name='static' file='../static_content.html'/>
<%inherit file="../main.html" />
<%block name="title"><title>${_("FAQ")}</title></%block>
<section class="container about">
<nav>
<a href="${reverse('about_edx')}">${_("Vision")}</a>
<a href="${reverse('faq_edx')}" class="active">${_("Faq")}</a>
<a href="${reverse('press')}">${_("Press")}</a>
<a href="${reverse('contact')}">${_("Contact")}</a>
</nav>
<section class="faq">
<section class="responses">
<section id="the-organization" class="category">
<h2>${_("Organization")}</h2>
<article class="response">
<h3>${_("What is {edX}?").format(edX="edX")}</h3>
<p>${_('{EdX} is a not-for-profit enterprise of its founding partners, the {MIT_long} ({MIT}) and {harvard_u} that offers online learning to on-campus students and to millions of people around the world. To do so, {edX} is building an open-source online learning platform and hosts an online web portal at <a href=\"http://www.edx.org\">www.edx.org</a> for online education.').format(EdX="EdX", edX="edX", MIT_long="Massachusetts Institute of Technology", MIT="MIT", harvard_u="Harvard University")}</p>
<p>${_("{EdX} currently offers {HarvardX}, {MITx} and {BerkeleyX} classes online for free. Beginning in fall 2013, {edX} will offer {WellesleyX} , {GeorgetownX} and the {UTexas} classes online for free. The {UT} System includes nine universities and six health institutions. In 2014, {edX} will further expand its consortium, including several international schools, when it begins offering courses from {EPFL}, {McGill}, {Toronto}, {ANU}, {Delft}, and {Rice}. The {edX} institutions aim to extend their collective reach to build a global community of online students. Along with offering online courses, the three universities undertake research on how students learn and how technology can transform learning both on-campus and online throughout the world.").format(
EdX="EdX",
edX="edX",
HarvardX="HarvardX",
MITx="MITx",
BerkeleyX="BerkeleyX",
WellesleyX="WellesleyX",
GeorgetownX="GeorgetownX",
UTexas="University of Texas System",
UT="UT",
EPFL=u"École Polytechnique Fédérale de Lausanne",
McGill="McGill University",
Toronto="University of Toronto",
ANU="Australian National University",
Delft="Delft University of Technology",
Rice="Rice University",
)}
</p>
</article>
<article class="response">
<h3>${_("Will {edX} be adding additional X Universities?").format(edX="edX")}</h3>
<p>${_("More than 200 institutions from around the world have expressed interest in collaborating with {edX} since {Harvard} and {MIT} announced its creation in May. {EdX} is focused above all on quality and developing the best not-for-profit model for online education. In addition to providing online courses on the {edX} platform, the {x_consortium} will be a forum in which members can share experiences around online learning. {Harvard}, {MIT}, {Berkeley}, the {UTexas} and the other {consortium} members will work collaboratively to establish the {x_consortium}, whose membership will expand to include additional \"{X_Universities}.\" As noted above, {edX}'s newest {consortium} members include {Wellesley}, {Georgetown}, {EPFL}, {McGill}, {Toronto}, {ANU}, {Delft}, and {Rice}. Each member of the {consortium} will offer courses on the {edX} platform as an \"{X_University}\". The gathering of many universities' educational content together on one site will enable learners worldwide to access the offered course content of any participating university from a single website, and to use a set of online educational tools shared by all participating universities.").format(
EdX="EdX",
edX="edX",
Harvard="Harvard",
MIT="MIT",
x_consortium="\"X University\" Consortium",
consortium="consortium",
X_Universities="X Universities",
X_University="X University",
Berkeley="UC Berkeley",
Wellesley="Wellesley",
Georgetown="Georgetown",
UTexas="University of Texas System",
EPFL=u"École Polytechnique Fédérale de Lausanne",
McGill="McGill University",
Toronto="University of Toronto",
ANU="Australian National University",
Delft="Delft University of Technology",
Rice="Rice University",
)}</p>
<p>${_("{EdX} will actively explore the addition of other institutions from around the world to the {edX} platform, and looks forward to adding more \"{X_Universities}\".").format(EdX="EdX", edX="edX", X_Universities="X Universities")}
</p>
</article>
</section>
<section id="students" class="category">
<h2>${_("Students")}</h2>
<article class="response">
<h3>${_("Who can take {edX} courses? Will there be an admissions process?").format(edX="edX")}</h3>
<p>${_("{EdX} will be available to anyone in the world with an internet connection, and in general, there will not be an admissions process.").format(EdX="EdX")}</p>
</article>
<article class="response">
<h3>${_("Will certificates be awarded?")}</h3>
<p>${_("Yes. Online learners who demonstrate mastery of subjects can earn a certificate "
"of mastery. Certificates will be issued at the discretion of {edX} and the underlying "
"\"{X_University}\" that offered the course under the name of the underlying \"{X_University}\" from where the course originated, i.e. {HarvardX}, {MITx} or {BerkeleyX}. "
"For the courses in Fall 2012, those certificates will be free. There is a plan to "
"charge a modest fee for certificates in the future. Note: At this time, {edX} is "
"holding certificates for learners connected with Cuba, Iran, Syria and Sudan "
"pending confirmation that the issuance is in compliance with U.S. embargoes.").format(edX="edX", X_University="X University", HarvardX="HarvardX", MITx="MITx", BerkeleyX="BerkeleyX")}</p>
</article>
<article class="response">
<h3>${_("What will the scope of the online courses be? How many? Which faculty?")}</h3>
<p>${_('Our goal is to offer a wide variety of courses across disciplines. There are currently {link_start}fifteen{link_end} offered on the {edX} platform.').format(link_start='<a href="/courses">', link_end='</a>', edX="edX")}</p>
</article>
<article class="response">
<h3>${_("Who is the learner? Domestic or international? Age range?")}</h3>
<p>${_("Improving teaching and learning for students on our campuses is one of our primary goals. Beyond that, we don't have a target group of potential learners, as the goal is to make these courses available to anyone in the world - from any demographic - who has interest in advancing their own knowledge. The only requirement is to have a computer with an internet connection. More than 150,000 students from over 160 countries registered for {MITx}'s first course, 6.002x: Circuits and Electronics. The age range of students certified in this course was from 14 to 74 years-old.").format(MITx="MITx")}</p>
</article>
<article class="response">
<h3>${_("Will participating universities' standards apply to all courses offered on the edX platform?")}</h3>
<p>${_("Yes: the reach changes exponentially, but the rigor remains the same.")}</p>
</article>
<article class="response">
<h3>${_("How do you intend to test whether this approach is improving learning?")}</h3>
<p>${_("{EdX} institutions have assembled faculty members who will collect and analyze data to assess results and the impact {edX} is having on learning.").format(EdX="EdX", edX="edX")}</p>
</article>
<article class="response">
<h3>${_("How may I apply to study with {edX}?").format(edX="edX")}</h3>
<p>${_('Simply complete the online {link_start}signup form{link_end}. Enrolling will create your unique student record in the {edX} database, allow you to register for classes, and to receive a certificate on successful completion.').format(link_start='<a href="#signup-modal" rel="leanModal">', link_end='</a>', edX="edX")}</p>
</article>
<article class="response">
<h3>${_("How may another university participate in {edX}? ").format(edX="edX")}</h3>
<p>${_('If you are from a university interested in discussing {edX}, please email {email}').format(email='<a href="mailto:university@edx.org">university@edx.org</a>', edX="edX")}</p>
</article>
</section>
<section id="technology-platform" class="category">
<h2>${_("Technology Platform")}</h2>
<article class="response">
<h3>${_("What technology will {edX} use?").format(edX="edX")}</h3>
<p>${_("The {edX} open-source online learning platform will feature interactive learning designed specifically for the web. Features will include: self-paced learning, online discussion groups, wiki-based collaborative learning, assessment of learning as a student progresses through a course, and online laboratories and other interactive learning tools. The platform will also serve as a laboratory from which data will be gathered to better understand how students learn. Because it is open source, the platform will be continuously improved by a worldwide community of collaborators, with new features added as needs arise.").format(edX="edX")}</p>
<p>${_("The first version of the technology was used in the first <em>{MITx}</em> course, 6.002x Circuits and Electronics, which launched in Spring, 2012.").format(MITx="MITx")}</p>
</article>
<article class="response">
<h3>${_("How is this different from what other universities are doing online?")}</h3>
<p>${_("{EdX} is a not-for-profit enterprise built upon the shared educational missions of its founding partners, {Harvard_long} and {MIT}. The {edX} platform will be available as open source. Also, a primary goal of {edX} is to improve teaching and learning on campus by experimenting with blended models of learning and by supporting faculty in conducting significant research on how students learn.").format(edX="edX", EdX="EdX", Harvard_long="Harvard University", MIT="MIT")}</p>
</article>
</section>
</section>
<nav class="categories">
<a href="#organization">${_("Organization")}</a>
<a href="${reverse('help_edx')}">${_("Students")}</a>
<a href="#technology-platform">${_("Technology Platform")}</a>
</nav>
</section>
</section>
%if user.is_authenticated():
<%include file="../signup_modal.html" />
%endif
<%! from django.utils.translation import ugettext as _ %>
<%! from django.core.urlresolvers import reverse %>
<%inherit file="../main.html" />
<%namespace name='static' file='../static_content.html'/>
<%block name="title"><title>Terms of Service</title></%block>
<section class="static-container tos">
<h1>edX Terms of Service</h1>
<hr class="horizontal-divider"/>
<div class="inner-wrapper">
<p><strong>NOTICE: on September 26, 2012 edX adopted amended Terms of Service, providing as follows:</strong></p>
<p>Welcome to edX. Please read these Terms of Service ("TOS") and edX's <a href="${reverse('privacy_edx')}">Privacy Policy</a> and <a href="${reverse('honor')}">Honor Code</a> prior to registering for edX.org or using any portion of the edX website (the "Site," which consists of all content and pages located within the edX.org web domain), including accessing any course material, chat rooms, or other electronic services. These TOS, the Privacy Policy and the Honor Code are agreements (the "Agreements") between you and edX. By using the Site, you accept and agree to be legally bound by the Agreements, whether or not you are a registered user. If you do not understand or do not wish to be bound by the terms of the Agreements, you should not use the Site.")</p>
<p>EdX reserves the right to modify these TOS at any time without advance notice. Any changes to these TOS will be effective immediately upon posting on this page, with an updated effective date. By accessing the Site after any changes have been made, you signify your agreement on a prospective basis to the modified TOS and all of the changes. Be sure to return to this page periodically to ensure familiarity with the most current version of these TOS.</p>
<h2>Description of edX</h2>
<p>EdX offers online courses that include opportunities for professor-to-student and student-to-student interactivity, individual assessment of a student's work and for students who demonstrate their mastery of subjects, a certificate of mastery.</p>
<h2>Rules for Online Conduct</h2>
<p>You agree that you are responsible for your own use of the Site and for your User Postings. "User Postings" include all content submitted, posted, published or distributed on the Site by you or other users of the Site, including but not limited to all forum posts, wiki edits, notes, questions, comments, videos and file uploads. You agree that you will use the Site in compliance with these TOS, the Honor Code and all applicable local, state, national and international laws, rules and regulations, including copyright laws, any laws regarding the transmission of technical data exported from your country of residence, and all United States export control laws.</p>
<p>As a condition of your use of the edX services, you will not use the Site in any manner intended to damage, disable, overburden or impair any edX server or the network(s) connected to any edX server or to interfere with any other party's use and enjoyment of the Site. You may not attempt to gain unauthorized access to the Site, other accounts, computer systems or networks connected to any edX server through hacking, password mining or any other means. You may not obtain or attempt to obtain any materials or information stored on the Site, its servers or associated computers through any means not intentionally made available through the Site.</p>
<h2>The following list of items is strictly prohibited on the Site:</h2>
<ol>
<li>Content that defames, harasses or threatens others;</li>
<li>Content that discusses illegal activities with the intent to commit them;</li>
<li>Content that infringes another's intellectual property, including, but not limited to, copyrights or trademarks;</li>
<li>Profane, pornographic, obscene, indecent or unlawful content;</li>
<li>Advertising or any form of commercial solicitation;</li>
<li>Content related to partisan political activities;</li>
<li>Viruses, trojan horses, worms, time bombs, corrupted files, malware, spyware or any other similar software that may damage the operation of another's computer or property; and</li>
<li>Content that contains intentionally inaccurate information or that is posted with the intent of misleading others.</li>
</ol>
<p>Furthermore, you agree not to scrape, or otherwise download in bulk, any Site content, including but not limited to a list or directory of users on the system, on-line textbooks, User Postings or user information. You agree not to misrepresent or attempt to misrepresent your identity while using the Sites (although you are welcome and encouraged to use an anonymous username in the forums and to act in a manner that keeps your identity concealed).</p>
<h2>User Accounts and Authority</h2>
<p>In order to participate fully in Site activities, you must provide your name, an email address and a user password in order to create a user account ("User Account"). You agree that you will never divulge or share access or access information to your User Account with any third party for any reason. In setting up your User Account, you may be prompted to enter additional optional information (e.g., your address). You represent that all information provided by you is accurate and current. You agree to maintain and update your information to keep it accurate and current.</p>
<p>We care about the confidentiality and security of your personal information. Please see our <a href="${reverse('privacy_edx')}">Privacy Policy</a> for more information about what information about you edX collects and how edX uses that information."</p>
<h2>Your Right to Use Content on the Site</h2>
<p>Unless indicated as being in the public domain, the content on the Site is protected by United States and foreign copyright laws. Unless otherwise expressly stated on the Site, the texts, exams, video, images and other instructional materials provided with the courses offered on this Site are for your personal use in connection with those courses only. MIT and Harvard aim to make much of the edX course content available under more open license terms that will help create a vibrant ecosystem of contributors and further edX's goal of making education accessible and affordable to the world.</p>
<p>Certain reference documents, digital textbooks, articles and other information on the Site are used with the permission of third parties, and use of that information is subject to certain rules and conditions, which will be posted along with the information. By using this Site you agree to abide by all such rules and conditions.</p>
<p>You agree to retain all copyright and other notices on any content you obtain from the Site. All rights in the Site and its content, if not expressly granted, are reserved.</p>
<h2>User Postings</h2>
<p><strong>User Postings Representations and Warranties.</strong> By submitting or distributing your User Postings, you affirm, represent and warrant (1) that you have the necessary rights, licenses, consents and/or permissions to reproduce and publish the User Postings and to authorize edX and its users to reproduce, modify, publish and otherwise use and distribute your User Postings in a manner consistent with the licenses granted by you below, and (2) that neither your submission of your User Postings nor the exercise of the licenses granted below will infringe or violate the rights of any third party. You, and not edX, are solely responsible for your User Postings and the consequences of posting or publishing them.</p>
<p><strong>License Grant to edX.</strong> By submitting or distributing User Postings to the Site, you hereby grant to edX a worldwide, non-exclusive, transferable, assignable, sublicensable, fully paid-up, royalty-free, perpetual, irrevocable right and license to host, transfer, display, perform, reproduce, modify, distribute, re-distribute, relicense and otherwise use, make available and exploit your User Postings, in whole or in part, in any form and in any media formats and through any media channels (now known or hereafter developed).</p>
<p><strong>License Grant to edX Users.</strong> By submitting or distributing User Postings to the Site, you hereby grant to each user of the Site a non-exclusive license to access and use your User Postings in connection with their use of the Site for their own personal purposes.</p>
<h2>Certificates, etc.</h2>
<p>EdX and/or the colleges and universities providing courses on the Site may offer a certificate of mastery or other acknowledgment (a "Certificate") for students who, in their judgment, have satisfactorily demonstrated mastery of the course material. Certificates will be issued by edX under the name of the underlying "X University" from where the course originated, i.e. HarvardX, MITx. etc. The decision whether a Certificate will be awarded to a given student will be solely within the discretion of the awarding entity, as will the name and form of any such Certificate. EdX and/or the institutions providing courses on the Site may choose not to offer a Certificate for some courses.</p>
<p>When you take a course through edX, you will not be an applicant for admission to, or enrolled in, any degree program of the institution as a result of registering for or completing a course through edX. You will not be entitled to use any of the resources of the institution beyond the online courses provided on the Site, nor will you be eligible to receive student privileges or benefits provided to students enrolled in degree programs of the institution.</p>
<h2>Trademarks</h2>
<p><strong>Use of edX, MIT, Harvard University and X University Names, Trademarks and Service Marks.</strong> The "edX", "MIT", and "Harvard University" names, logos and seals are trademarks ("Trademarks") of the respective entities. Likewise, the names, logos, and seals of the other colleges and universities providing courses on the Site (the "X Universities") are Trademarks owned by the X Universities. You may not use any of these Trademarks, or any variations thereof, without the owner's prior written consent. You may not use any of these Trademarks, or any variations thereof, for promotional purposes, or in any way that deliberately or inadvertently claims, suggests or, in these institutions' sole judgment, gives the appearance or impression of a relationship with or endorsement by these institutions.</p>
<p>All Trademarks not owned by these institutions that appear on the Site or on or through the services made available on or through the Site, if any, are the property of their respective owners.</p>
<p>Nothing contained on the Site should be construed as granting, by implication, estoppel or otherwise, any license or right to use any Trademark displayed on the Site without the written permission of the owner of the applicable Trademark.</p>
<h2>Digital Millennium Copyright Act</h2>
<p>Copyright owners who believe their material has been infringed on the Site should contact edX's designated copyright agent at <a href="mailto:dcma-agent@mit.edu">dcma-agent@mit.edu</a> or at 77 Massachusetts Avenue, Cambridge, MA 02138-4307 Attention: MIT DCMA Agent, W92-263A.</p>
<p>Notification must include:</p>
<ul>
<li>Identification of the copyrighted work, or, in the case of multiple works at the same location, a representative list of such works at that site.</li>
<li>Identification of the material that is claimed to be infringing or to be the subject of infringing activity. You must include sufficient information for us to locate the material (e.g., URL, IP address, computer name).</li>
<li>Information for us to be able to contact the complaining party (e.g., email address, phone number).</li>
<li>A statement that the complaining party believes that the use of the material has not been authorized by the copyright owner or an authorized agent.</li>
<li>A statement that the information in the notification is accurate and that the complaining party is authorized to act on behalf of the copyright owner.</li>
</ul>
<h2>Disclaimers of Warranty / Limitations of Liabilities</h2>
<p><strong>THE SITE AND ANY INFORMATION, CONTENT OR SERVICES MADE AVAILABLE ON OR THROUGH THE SITE ARE PROVIDED "AS IS" AND "AS AVAILABLE" WITHOUT WARRANTY OF ANY KIND (EXPRESS, IMPLIED OR OTHERWISE), INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT, EXCEPT INSOFAR AS ANY SUCH IMPLIED WARRANTIES MAY NOT BE DISCLAIMED UNDER APPLICABLE LAW.</strong></p>
<p><strong>EDX AND THE EDX PARTICIPANTS (AS HERINAFTER DEFINED) DO NOT WARRANT THAT THE SITE WILL OPERATE IN AN UNINTERRUPTED OR ERROR-FREE MANNER, THAT THE SITE IS FREE OF VIRUSES OR OTHER HARMFUL COMPONENTS, OR THAT THE COURSES OR CONTENT PROVIDED WILL MEET YOUR NEEDS OR EXPECTATIONS. EDX AND THE EDX PARTICIPANTS ALSO MAKE NO WARRANTY ABOUT THE ACCURACY, COMPLETENESS, TIMELINESS, OR QUALITY OF THE SITE OR ANY COURSES OR CONTENT, OR THAT ANY PARTICULAR COURSES OR CONTENT WILL CONTINUE TO BE MADE AVAILABLE. &ldquo;EDX PARTICIPANTS&rdquo; MEANS MIT, HARVARD, X UNIVERSITIES, THE ENTITIES PROVIDING INFORMATION, CONTENT OR SERVICES FOR THE SITE, THE COURSE INSTRUCTORS AND THEIR STAFFS.</strong></p>
<p><strong>USE OF THE SITE, AND THE CONTENT AND SERVICES OBTAINED FROM OR THROUGH THE SITE, IS AT YOUR OWN RISK. YOUR ACCESS TO OR DOWNLOAD OF INFORMATION, MATERIALS OR DATA THROUGH THE SITE OR ANY REFERENCE SITES IS AT YOUR OWN DISCRETION AND RISK, AND YOU WILL BE SOLELY RESPONSIBLE FOR ANY DAMAGE TO YOUR PROPERTY (INCLUDING YOUR COMPUTER SYSTEM) OR LOSS OF DATA THAT RESULTS FROM THE DOWNLOAD OR USE OF SUCH MATERIAL OR DATA.</strong></p>
<p><strong>User Postings Disclaimer.</strong> You understand that when using the Site you will be exposed to User Postings from a variety of sources and that neither edX nor the edX Participants are responsible for the accuracy, usefulness, reliability or intellectual property rights of or relating to such User Postings. You further understand and acknowledge that you may be exposed to User Postings that are inaccurate, offensive, defamatory, indecent or objectionable and you agree to waive, and hereby do waive, any legal or equitable rights or remedies you have or may have against edX or any of the edX Participants with respect thereto. Neither edX nor the edX Participants endorse any User Postings or any opinion, recommendation or advice expressed therein. Neither edX nor the edX Participants have any obligation to monitor any User Postings or any other user communications through the Site.</p>
<p>However, edX reserves the right to review User Postings and to exercise its sole discretion to edit or remove, in whole or in part, any User Posting at any time or for any reason, or to allow the edX Participants to do so. Without limiting the foregoing, upon receiving notice from a user or a content owner that a User Posting allegedly does not conform to these TOS, edX may investigate the allegation and determine in its sole discretion whether to remove the User Posting, which it reserves the right to do at any time and without notice.</p>
<p><strong>Links to Other Sites.</strong> The Site may include hyperlinks to sites maintained or controlled by others. EdX and the edX Participants are not responsible for and do not routinely screen, approve, review or endorse the contents of or use of any of the products or services that may be offered at these sites. If you decide to access linked third party web sites, you do so at your own risk.</p>
<p><strong>TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, YOU AGREE THAT NEITHER EDX NOR ANY OF THE EDX PARTICIPANTS WILL BE LIABLE TO YOU FOR ANY LOSS OR DAMAGES, EITHER ACTUAL OR CONSEQUENTIAL, ARISING OUT OF OR RELATING TO THESE TERMS OF SERVICE, OR YOUR (OR ANY THIRD PARTY'S) USE OF OR INABILITY TO USE THE SITE, OR YOUR PLACEMENT OF CONTENT ON THE SITE, OR YOUR RELIANCE UPON INFORMATION OBTAINED FROM OR THROUGH THE SITE, WHETHER YOUR CLAIM IS BASED IN CONTRACT, TORT, STATUTORY OR OTHER LAW.</strong></p>
<p><strong>IN PARTICULAR, TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, NEITHER EDX NOR ANY OF THE EDX PARTICIPANTS WILL HAVE ANY LIABILITY FOR ANY CONSEQUENTIAL, INDIRECT, PUNITIVE, SPECIAL, EXEMPLARY OR INCIDENTAL DAMAGES, WHETHER FORESEEABLE OR UNFORESEEABLE AND WHETHER OR NOT EDX OR ANY OF THE EDX PARTICIPANTS HAS BEEN NEGLIGENT OR OTHERWISE AT FAULT (INCLUDING, BUT NOT LIMITED TO, CLAIMS FOR DEFAMATION, ERRORS, LOSS OF PROFITS, LOSS OF DATA OR INTERRUPTION IN AVAILABILITY OF DATA).</strong></p>
<h2>Indemnification</h2>
<p>You agree to defend, hold harmless and indemnify edX and the edX Participants, and their respective subsidiaries, affiliates, officers, faculty, students, fellows, governing board members, agents and employees from and against any third-party claims, actions or demands arising out of, resulting from or in any way related to your use of the Site, including any liability or expense arising from any and all claims, losses, damages (actual and consequential), suits, judgments, litigation costs and attorneys' fees, of every kind and nature. In such a case, edX or one of the edX Participants will provide you with written notice of such claim, suit or action.</p>
<h2>Miscellaneous</h2>
<p><strong>Termination Rights; Discontinuation of Courses and Content.</strong> You agree that edX, in its sole discretion, may terminate your use of the Site or your participation in it, for any reason or no reason, upon notice to you. It is edX's policy to terminate in appropriate circumstances users of the Site who are repeat copyright infringers. EdX and the edX Participants reserve the right at any time in their sole discretion to cancel, delay, reschedule or alter the format of any course offered through edX, or to cease providing any part or all of the Site content or related services, and you agree that neither edX nor any of the edX Participants will have any liability to you for such an action. If you no longer desire to participate in the Site, you may terminate your participation at any time. The rights granted to you hereunder will terminate upon any termination of your right to use the Site, but the other provisions of these TOS will survive any such termination. </p>
<p><strong>Entire Agreement.</strong> These TOS, the Honor Code, and the Privacy Policy together constitute the entire agreement between you and edX with respect to your use of the Site, superseding any prior agreements between you and edX regarding your use of the Site.</p>
<p><strong>Waiver and Severability of TOS.</strong> The failure of edX to exercise or enforce any right or provision of these TOS shall not constitute a waiver of such right or provision. If any provision of these TOS is found by a court of competent jurisdiction to be invalid, the parties nevertheless agree that the court should endeavor to give effect to the parties' intentions as reflected in the provision and the other provisions of these TOS shall remain in full force and effect.</p>
<p><strong>Choice of Law/Forum Selection.</strong> You agree that these TOS and any claim or dispute arising out of or relating to these TOS or any content or service obtained from or through the Site will be governed by the laws of the Commonwealth of Massachusetts, excluding its conflicts of law provisions. You agree that all such claims and disputes will be heard and resolved exclusively in the federal or state courts located in and serving Cambridge, Massachusetts, U.S.A. You consent to the personal jurisdiction of those courts over you for this purpose, and you waive and agree not to assert any objection to such proceedings in those courts (including any defense or objection of lack of proper jurisdiction or venue or inconvenience of forum).</p>
<p><strong>Effective Date:</strong> September 26, 2012</p>
</div>
</section>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment