diff --git a/common/djangoapps/contentserver/admin.py b/common/djangoapps/contentserver/admin.py
index 893b267f0b..aa385279f8 100644
--- a/common/djangoapps/contentserver/admin.py
+++ b/common/djangoapps/contentserver/admin.py
@@ -4,7 +4,7 @@ that gets used when sending cachability headers back with request course assets.
"""
from django.contrib import admin
from config_models.admin import ConfigurationModelAdmin
-from .models import CourseAssetCacheTtlConfig, CdnUserAgentsConfig
+from .models import CourseAssetCacheTtlConfig
class CourseAssetCacheTtlConfigAdmin(ConfigurationModelAdmin):
@@ -26,24 +26,4 @@ class CourseAssetCacheTtlConfigAdmin(ConfigurationModelAdmin):
return self.list_display
-class CdnUserAgentsConfigAdmin(ConfigurationModelAdmin):
- """
- Basic configuration for CDN user agent whitelist.
- """
- list_display = [
- 'cdn_user_agents'
- ]
-
- def get_list_display(self, request):
- """
- Restore default list_display behavior.
-
- ConfigurationModelAdmin overrides this, but in a way that doesn't
- respect the ordering. This lets us customize it the usual Django admin
- way.
- """
- return self.list_display
-
-
admin.site.register(CourseAssetCacheTtlConfig, CourseAssetCacheTtlConfigAdmin)
-admin.site.register(CdnUserAgentsConfig, CdnUserAgentsConfigAdmin)
diff --git a/common/djangoapps/contentserver/middleware.py b/common/djangoapps/contentserver/middleware.py
index bd6088093f..433967e1cb 100644
--- a/common/djangoapps/contentserver/middleware.py
+++ b/common/djangoapps/contentserver/middleware.py
@@ -3,14 +3,13 @@ Middleware to serve assets.
"""
import logging
-import datetime
-import newrelic.agent
+import datetime
from django.http import (
HttpResponse, HttpResponseNotModified, HttpResponseForbidden,
HttpResponseBadRequest, HttpResponseNotFound)
from student.models import CourseEnrollment
-from contentserver.models import CourseAssetCacheTtlConfig, CdnUserAgentsConfig
+from contentserver.models import CourseAssetCacheTtlConfig
from header_control import force_header_for_response
from xmodule.assetstore.assetmgr import AssetManager
@@ -56,19 +55,6 @@ class StaticContentServer(object):
except (ItemNotFoundError, NotFoundError):
return HttpResponseNotFound()
- # Set the basics for this request.
- newrelic.agent.add_custom_parameter('course_id', loc.course_key)
- newrelic.agent.add_custom_parameter('org', loc.org)
- newrelic.agent.add_custom_parameter('contentserver.path', loc.path)
-
- # Figure out if this is a CDN using us as the origin.
- is_from_cdn = StaticContentServer.is_cdn_request(request)
- newrelic.agent.add_custom_parameter('contentserver.from_cdn', True if is_from_cdn else False)
-
- # Check if this content is locked or not.
- locked = self.is_content_locked(content)
- newrelic.agent.add_custom_parameter('contentserver.locked', True if locked else False)
-
# Check that user has access to the content.
if not self.is_user_authorized(request, content, loc):
return HttpResponseForbidden('Unauthorized')
@@ -121,11 +107,8 @@ class StaticContentServer(object):
response['Content-Range'] = 'bytes {first}-{last}/{length}'.format(
first=first, last=last, length=content.length
)
- range_len = last - first + 1
- response['Content-Length'] = str(range_len)
+ response['Content-Length'] = str(last - first + 1)
response.status_code = 206 # Partial Content
-
- newrelic.agent.add_custom_parameter('contentserver.range_len', range_len)
else:
log.warning(
u"Cannot satisfy ranges in Range header: %s for content: %s", header_value, unicode(loc)
@@ -137,9 +120,6 @@ class StaticContentServer(object):
response = HttpResponse(content.stream_data())
response['Content-Length'] = content.length
- newrelic.agent.add_custom_parameter('contentserver.content_len', content.length)
- newrelic.agent.add_custom_parameter('contentserver.content_type', content.content_type)
-
# "Accept-Ranges: bytes" tells the user that only "bytes" ranges are allowed
response['Accept-Ranges'] = 'bytes'
response['Content-Type'] = content.content_type
@@ -166,11 +146,9 @@ class StaticContentServer(object):
# indicate there should be no caching whatsoever.
cache_ttl = CourseAssetCacheTtlConfig.get_cache_ttl()
if cache_ttl > 0 and not is_locked:
- newrelic.agent.add_custom_parameter('contentserver.cacheable', True)
response['Expires'] = StaticContentServer.get_expiration_value(datetime.datetime.utcnow(), cache_ttl)
response['Cache-Control'] = "public, max-age={ttl}, s-maxage={ttl}".format(ttl=cache_ttl)
elif is_locked:
- newrelic.agent.add_custom_parameter('contentserver.cacheable', False)
response['Cache-Control'] = "private, no-cache, no-store"
response['Last-Modified'] = content.last_modified_at.strftime(HTTP_DATE_FORMAT)
@@ -180,39 +158,19 @@ class StaticContentServer(object):
# caches a version of the response without CORS headers, in turn breaking XHR requests.
force_header_for_response(response, 'Vary', 'Origin')
- @staticmethod
- def is_cdn_request(request):
- """
- Attempts to determine whether or not the given request is coming from a CDN.
-
- Currently, this is a static check because edx.org only uses CloudFront, but may
- be expanded in the future.
- """
- cdn_user_agents = CdnUserAgentsConfig.get_cdn_user_agents()
- user_agent = request.META.get('HTTP_USER_AGENT', '')
- if user_agent in cdn_user_agents:
- # This is a CDN request.
- return True
-
- return False
-
@staticmethod
def get_expiration_value(now, cache_ttl):
"""Generates an RFC1123 datetime string based on a future offset."""
expire_dt = now + datetime.timedelta(seconds=cache_ttl)
return expire_dt.strftime(HTTP_DATE_FORMAT)
- def is_content_locked(self, content):
- """
- Determines whether or not the given content is locked.
- """
- return getattr(content, "locked", False)
-
def is_user_authorized(self, request, content, location):
"""
Determines whether or not the user for this request is authorized to view the given asset.
"""
- if not self.is_content_locked(content):
+
+ is_locked = getattr(content, "locked", False)
+ if not is_locked:
return True
if not hasattr(request, "user") or not request.user.is_authenticated():
diff --git a/common/djangoapps/contentserver/migrations/0002_cdnuseragentsconfig.py b/common/djangoapps/contentserver/migrations/0002_cdnuseragentsconfig.py
deleted file mode 100644
index a078ae35e5..0000000000
--- a/common/djangoapps/contentserver/migrations/0002_cdnuseragentsconfig.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-import django.db.models.deletion
-from django.conf import settings
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ('contentserver', '0001_initial'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='CdnUserAgentsConfig',
- fields=[
- ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
- ('change_date', models.DateTimeField(auto_now_add=True, verbose_name='Change date')),
- ('enabled', models.BooleanField(default=False, verbose_name='Enabled')),
- ('cdn_user_agents', models.TextField(default=b'Amazon CloudFront', help_text=b'A newline-separated list of user agents that should be considered CDNs.')),
- ('changed_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, editable=False, to=settings.AUTH_USER_MODEL, null=True, verbose_name='Changed by')),
- ],
- ),
- ]
diff --git a/common/djangoapps/contentserver/models.py b/common/djangoapps/contentserver/models.py
index 013ff364f1..1d8dbfa647 100644
--- a/common/djangoapps/contentserver/models.py
+++ b/common/djangoapps/contentserver/models.py
@@ -2,7 +2,7 @@
Models for contentserver
"""
-from django.db.models.fields import PositiveIntegerField, TextField
+from django.db.models.fields import PositiveIntegerField
from config_models.models import ConfigurationModel
@@ -27,26 +27,3 @@ class CourseAssetCacheTtlConfig(ConfigurationModel):
def __unicode__(self):
return unicode(repr(self))
-
-
-class CdnUserAgentsConfig(ConfigurationModel):
- """Configuration for the user agents we expect to see from CDNs."""
-
- class Meta(object):
- app_label = 'contentserver'
-
- cdn_user_agents = TextField(
- default='Amazon CloudFront',
- help_text="A newline-separated list of user agents that should be considered CDNs."
- )
-
- @classmethod
- def get_cdn_user_agents(cls):
- """Gets the list of CDN user agents, if present"""
- return cls.current().cdn_user_agents
-
- def __repr__(self):
- return ''.format(self.get_cdn_user_agents())
-
- def __unicode__(self):
- return unicode(repr(self))
diff --git a/common/djangoapps/contentserver/test/test_contentserver.py b/common/djangoapps/contentserver/test/test_contentserver.py
index c68bc5a63c..9c6e79e318 100644
--- a/common/djangoapps/contentserver/test/test_contentserver.py
+++ b/common/djangoapps/contentserver/test/test_contentserver.py
@@ -10,7 +10,6 @@ import unittest
from uuid import uuid4
from django.conf import settings
-from django.test import RequestFactory
from django.test.client import Client
from django.test.utils import override_settings
from mock import patch
@@ -271,49 +270,6 @@ class ContentStoreToyCourseTest(SharedModuleStoreTestCase):
near_expire_dt = StaticContentServer.get_expiration_value(start_dt, 55)
self.assertEqual("Thu, 01 Dec 1983 20:00:55 GMT", near_expire_dt)
- @patch('contentserver.models.CdnUserAgentsConfig.get_cdn_user_agents')
- def test_cache_is_cdn_with_normal_request(self, mock_get_cdn_user_agents):
- """
- Tests that when a normal request is made -- i.e. from an end user with their
- browser -- that we don't classify the request as coming from a CDN.
- """
- mock_get_cdn_user_agents.return_value = 'Amazon CloudFront'
-
- request_factory = RequestFactory()
- browser_request = request_factory.get('/fake', HTTP_USER_AGENT='Chrome 1234')
-
- is_from_cdn = StaticContentServer.is_cdn_request(browser_request)
- self.assertEqual(is_from_cdn, False)
-
- @patch('contentserver.models.CdnUserAgentsConfig.get_cdn_user_agents')
- def test_cache_is_cdn_with_cdn_request(self, mock_get_cdn_user_agents):
- """
- Tests that when a CDN request is made -- i.e. from an edge node back to the
- origin -- that we classify the request as coming from a CDN.
- """
- mock_get_cdn_user_agents.return_value = 'Amazon CloudFront'
-
- request_factory = RequestFactory()
- browser_request = request_factory.get('/fake', HTTP_USER_AGENT='Amazon CloudFront')
-
- is_from_cdn = StaticContentServer.is_cdn_request(browser_request)
- self.assertEqual(is_from_cdn, True)
-
- @patch('contentserver.models.CdnUserAgentsConfig.get_cdn_user_agents')
- def test_cache_is_cdn_with_cdn_request_multiple_user_agents(self, mock_get_cdn_user_agents):
- """
- Tests that when a CDN request is made -- i.e. from an edge node back to the
- origin -- that we classify the request as coming from a CDN when multiple UAs
- are configured.
- """
- mock_get_cdn_user_agents.return_value = 'Amazon CloudFront\nAkamai GHost'
-
- request_factory = RequestFactory()
- browser_request = request_factory.get('/fake', HTTP_USER_AGENT='Amazon CloudFront')
-
- is_from_cdn = StaticContentServer.is_cdn_request(browser_request)
- self.assertEqual(is_from_cdn, True)
-
@ddt.ddt
class ParseRangeHeaderTestCase(unittest.TestCase):
diff --git a/common/djangoapps/course_modes/tests/test_views.py b/common/djangoapps/course_modes/tests/test_views.py
index a4e6af1ab4..b13d45f2b9 100644
--- a/common/djangoapps/course_modes/tests/test_views.py
+++ b/common/djangoapps/course_modes/tests/test_views.py
@@ -22,7 +22,7 @@ from student.tests.factories import CourseEnrollmentFactory, UserFactory
from student.models import CourseEnrollment
import lms.djangoapps.commerce.tests.test_utils as ecomm_test_utils
from course_modes.models import CourseMode, Mode
-from openedx.core.djangoapps.theming.test_util import with_comprehensive_theme
+from openedx.core.djangoapps.theming.test_util import with_is_edx_domain
@ddt.ddt
@@ -352,7 +352,7 @@ class CourseModeViewTest(UrlResetMixin, ModuleStoreTestCase):
self.assertEquals(course_modes, expected_modes)
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
- @with_comprehensive_theme("edx.org")
+ @with_is_edx_domain(True)
def test_hide_nav(self):
# Create the course modes
for mode in ["honor", "verified"]:
diff --git a/common/djangoapps/edxmako/paths.py b/common/djangoapps/edxmako/paths.py
index ce0cb81b92..3e7bb40430 100644
--- a/common/djangoapps/edxmako/paths.py
+++ b/common/djangoapps/edxmako/paths.py
@@ -9,14 +9,9 @@ import pkg_resources
from django.conf import settings
from mako.lookup import TemplateLookup
-from mako.exceptions import TopLevelLookupException
+from microsite_configuration import microsite
from . import LOOKUP
-from openedx.core.djangoapps.theming.helpers import (
- get_template as themed_template,
- get_template_path_with_theme,
- strip_site_theme_templates_path,
-)
class DynamicTemplateLookup(TemplateLookup):
@@ -54,25 +49,15 @@ class DynamicTemplateLookup(TemplateLookup):
def get_template(self, uri):
"""
- Overridden method for locating a template in either the database or the site theme.
-
- If not found, template lookup will be done in comprehensive theme for current site
- by prefixing path to theme.
- e.g if uri is `main.html` then new uri would be something like this `/red-theme/lms/static/main.html`
-
- If still unable to find a template, it will fallback to the default template directories after stripping off
- the prefix path to theme.
+ Overridden method which will hand-off the template lookup to the microsite subsystem
"""
- template = themed_template(uri)
+ microsite_template = microsite.get_template(uri)
- if not template:
- try:
- template = super(DynamicTemplateLookup, self).get_template(get_template_path_with_theme(uri))
- except TopLevelLookupException:
- # strip off the prefix path to theme and look in default template dirs
- template = super(DynamicTemplateLookup, self).get_template(strip_site_theme_templates_path(uri))
-
- return template
+ return (
+ microsite_template
+ if microsite_template
+ else super(DynamicTemplateLookup, self).get_template(uri)
+ )
def clear_lookups(namespace):
diff --git a/common/djangoapps/edxmako/shortcuts.py b/common/djangoapps/edxmako/shortcuts.py
index 895420e32b..da5ddfe3cd 100644
--- a/common/djangoapps/edxmako/shortcuts.py
+++ b/common/djangoapps/edxmako/shortcuts.py
@@ -12,18 +12,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import logging
-
-from django.conf import settings
-from django.core.urlresolvers import reverse
-from django.http import HttpResponse
from django.template import Context
+from django.http import HttpResponse
+import logging
from microsite_configuration import microsite
from edxmako import lookup_template
from edxmako.middleware import get_template_request_context
-from openedx.core.djangoapps.theming.helpers import get_template_path
+from django.conf import settings
+from django.core.urlresolvers import reverse
log = logging.getLogger(__name__)
@@ -115,7 +113,8 @@ def microsite_footer_context_processor(request):
def render_to_string(template_name, dictionary, context=None, namespace='main'):
- template_name = get_template_path(template_name)
+ # see if there is an override template defined in the microsite
+ template_name = microsite.get_template_path(template_name)
context_instance = Context(dictionary)
# add dictionary to context_instance
diff --git a/common/djangoapps/edxmako/tests.py b/common/djangoapps/edxmako/tests.py
index fc31ace82f..12ccb54274 100644
--- a/common/djangoapps/edxmako/tests.py
+++ b/common/djangoapps/edxmako/tests.py
@@ -116,12 +116,8 @@ class MakoMiddlewareTest(TestCase):
Test render_to_string() when makomiddleware has not initialized
the threadlocal REQUEST_CONTEXT.context. This is meant to run in LMS.
"""
- with patch("openedx.core.djangoapps.theming.helpers.get_current_site", return_value=None):
- del context_mock.context
- self.assertIn(
- "this module is temporarily unavailable",
- render_to_string("courseware/error-message.html", None),
- )
+ del context_mock.context
+ self.assertIn("this module is temporarily unavailable", render_to_string("courseware/error-message.html", None))
@unittest.skipUnless(settings.ROOT_URLCONF == 'cms.urls', 'Test only valid in cms')
@patch("edxmako.middleware.REQUEST_CONTEXT")
@@ -130,9 +126,8 @@ class MakoMiddlewareTest(TestCase):
Test render_to_string() when makomiddleware has not initialized
the threadlocal REQUEST_CONTEXT.context. This is meant to run in CMS.
"""
- with patch("openedx.core.djangoapps.theming.helpers.get_current_site", return_value=None):
- del context_mock.context
- self.assertIn("We're having trouble rendering your component", render_to_string("html_error.html", None))
+ del context_mock.context
+ self.assertIn("We're having trouble rendering your component", render_to_string("html_error.html", None))
def mako_middleware_process_request(request):
diff --git a/common/djangoapps/microsite_configuration/backends/base.py b/common/djangoapps/microsite_configuration/backends/base.py
index 43743ee935..a8fec36caa 100644
--- a/common/djangoapps/microsite_configuration/backends/base.py
+++ b/common/djangoapps/microsite_configuration/backends/base.py
@@ -11,6 +11,7 @@ BaseMicrositeTemplateBackend is Base Class for the microsite template backend.
from __future__ import absolute_import
import abc
+import edxmako
import os.path
import threading
@@ -271,7 +272,9 @@ class BaseMicrositeBackend(AbstractBaseMicrositeBackend):
Configure the paths for the microsites feature
"""
microsites_root = settings.MICROSITE_ROOT_DIR
+
if os.path.isdir(microsites_root):
+ edxmako.paths.add_lookup('main', microsites_root)
settings.STATICFILES_DIRS.insert(0, microsites_root)
log.info('Loading microsite path at %s', microsites_root)
@@ -289,7 +292,6 @@ class BaseMicrositeBackend(AbstractBaseMicrositeBackend):
microsites_root = settings.MICROSITE_ROOT_DIR
if self.has_configuration_set():
- settings.MAKO_TEMPLATES['main'].insert(0, microsites_root)
settings.DEFAULT_TEMPLATE_ENGINE['DIRS'].append(microsites_root)
diff --git a/common/djangoapps/microsite_configuration/tests/backends/test_database.py b/common/djangoapps/microsite_configuration/tests/backends/test_database.py
index d643dfe695..43a96cf19d 100644
--- a/common/djangoapps/microsite_configuration/tests/backends/test_database.py
+++ b/common/djangoapps/microsite_configuration/tests/backends/test_database.py
@@ -105,23 +105,6 @@ class DatabaseMicrositeBackendTests(DatabaseMicrositeTestCase):
microsite.clear()
self.assertIsNone(microsite.get_value('platform_name'))
- def test_enable_microsites_pre_startup(self):
- """
- Tests microsite.test_enable_microsites_pre_startup works as expected.
- """
- # remove microsite root directory paths first
- settings.DEFAULT_TEMPLATE_ENGINE['DIRS'] = [
- path for path in settings.DEFAULT_TEMPLATE_ENGINE['DIRS']
- if path != settings.MICROSITE_ROOT_DIR
- ]
- with patch.dict('django.conf.settings.FEATURES', {'USE_MICROSITES': False}):
- microsite.enable_microsites_pre_startup(log)
- self.assertNotIn(settings.MICROSITE_ROOT_DIR, settings.DEFAULT_TEMPLATE_ENGINE['DIRS'])
- with patch.dict('django.conf.settings.FEATURES', {'USE_MICROSITES': True}):
- microsite.enable_microsites_pre_startup(log)
- self.assertIn(settings.MICROSITE_ROOT_DIR, settings.DEFAULT_TEMPLATE_ENGINE['DIRS'])
- self.assertIn(settings.MICROSITE_ROOT_DIR, settings.MAKO_TEMPLATES['main'])
-
@patch('edxmako.paths.add_lookup')
def test_enable_microsites(self, add_lookup):
"""
@@ -139,6 +122,7 @@ class DatabaseMicrositeBackendTests(DatabaseMicrositeTestCase):
with patch.dict('django.conf.settings.FEATURES', {'USE_MICROSITES': True}):
microsite.enable_microsites(log)
self.assertIn(settings.MICROSITE_ROOT_DIR, settings.STATICFILES_DIRS)
+ add_lookup.assert_called_once_with('main', settings.MICROSITE_ROOT_DIR)
def test_get_all_configs(self):
"""
diff --git a/common/djangoapps/pipeline_mako/templates/static_content.html b/common/djangoapps/pipeline_mako/templates/static_content.html
index 9a2754f892..20e527f5c9 100644
--- a/common/djangoapps/pipeline_mako/templates/static_content.html
+++ b/common/djangoapps/pipeline_mako/templates/static_content.html
@@ -4,13 +4,7 @@ from pipeline_mako import compressed_css, compressed_js
from django.utils.translation import get_language_bidi
from mako.exceptions import TemplateLookupException
-from openedx.core.djangoapps.theming.helpers import (
- get_page_title_breadcrumbs,
- get_value,
- get_template_path,
- get_themed_template_path,
- is_request_in_themed_site,
-)
+from openedx.core.djangoapps.theming.helpers import get_page_title_breadcrumbs, get_value, get_template_path, get_themed_template_path, is_request_in_themed_site
from certificates.api import get_asset_url_by_slug
from lang_pref.api import released_languages
%>
diff --git a/common/djangoapps/student/tests/test_email.py b/common/djangoapps/student/tests/test_email.py
index eb86f3290c..807222f1d9 100644
--- a/common/djangoapps/student/tests/test_email.py
+++ b/common/djangoapps/student/tests/test_email.py
@@ -21,7 +21,7 @@ from edxmako.shortcuts import render_to_string
from edxmako.tests import mako_middleware_process_request
from util.request import safe_get_host
from util.testing import EventTestMixin
-from openedx.core.djangoapps.theming.test_util import with_comprehensive_theme
+from openedx.core.djangoapps.theming.test_util import with_is_edx_domain
class TestException(Exception):
@@ -99,7 +99,7 @@ class ActivationEmailTests(TestCase):
self._create_account()
self._assert_activation_email(self.ACTIVATION_SUBJECT, self.OPENEDX_FRAGMENTS)
- @with_comprehensive_theme("edx.org")
+ @with_is_edx_domain(True)
def test_activation_email_edx_domain(self):
self._create_account()
self._assert_activation_email(self.ACTIVATION_SUBJECT, self.EDX_DOMAIN_FRAGMENTS)
diff --git a/common/djangoapps/student/tests/tests.py b/common/djangoapps/student/tests/tests.py
index d93bb56a4e..25c9688ef6 100644
--- a/common/djangoapps/student/tests/tests.py
+++ b/common/djangoapps/student/tests/tests.py
@@ -45,6 +45,7 @@ from certificates.tests.factories import GeneratedCertificateFactory # pylint:
from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification
import shoppingcart # pylint: disable=import-error
from openedx.core.djangoapps.programs.tests.mixins import ProgramsApiConfigMixin
+from openedx.core.djangoapps.theming.test_util import with_is_edx_domain
# Explicitly import the cache from ConfigurationModel so we can reset it after each test
from config_models.models import cache
@@ -495,6 +496,7 @@ class DashboardTest(ModuleStoreTestCase):
self.assertEquals(response_2.status_code, 200)
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
+ @with_is_edx_domain(True)
def test_dashboard_header_nav_has_find_courses(self):
self.client.login(username="jack", password="test")
response = self.client.get(reverse("dashboard"))
diff --git a/common/lib/xmodule/xmodule/js/spec/capa/display_spec.coffee b/common/lib/xmodule/xmodule/js/spec/capa/display_spec.coffee
index af758f5b96..2edb056bd9 100644
--- a/common/lib/xmodule/xmodule/js/spec/capa/display_spec.coffee
+++ b/common/lib/xmodule/xmodule/js/spec/capa/display_spec.coffee
@@ -247,87 +247,6 @@ describe 'Problem', ->
runs ->
expect(@problem.checkButtonLabel.text).toHaveBeenCalledWith 'Check'
- describe 'check button on problems', ->
- beforeEach ->
- @problem = new Problem($('.xblock-student_view'))
- @checkDisabled = (v) -> expect(@problem.checkButton.hasClass('is-disabled')).toBe(v)
-
- describe 'some basic tests for check button', ->
- it 'should become enabled after a value is entered into the text box', ->
- $('#input_example_1').val('test').trigger('input')
- @checkDisabled false
- $('#input_example_1').val('').trigger('input')
- @checkDisabled true
-
- describe 'some advanced tests for check button', ->
- it 'should become enabled after a checkbox is checked', ->
- html = '''
-
-
-
-
-
- '''
- $('#input_example_1').replaceWith(html)
- @problem.checkAnswersAndCheckButton true
- @checkDisabled true
- $('#input_1_1_1').attr('checked', true).trigger('click')
- @checkDisabled false
- $('#input_1_1_1').attr('checked', false).trigger('click')
- @checkDisabled true
-
- it 'should become enabled after a radiobutton is checked', ->
- html = '''
-
-
-
-
-
- '''
- $('#input_example_1').replaceWith(html)
- @problem.checkAnswersAndCheckButton true
- @checkDisabled true
- $('#input_1_1_1').attr('checked', true).trigger('click')
- @checkDisabled false
- $('#input_1_1_1').attr('checked', false).trigger('click')
- @checkDisabled true
-
- it 'should become enabled after a value is selected in a selector', ->
- html = '''
-
-
-
- '''
- $('#input_example_1').replaceWith(html)
- @problem.checkAnswersAndCheckButton true
- @checkDisabled true
- $("#problem_sel select").val("val2").trigger('change')
- @checkDisabled false
- $("#problem_sel select").val("val0").trigger('change')
- @checkDisabled true
-
- it 'should become enabled after a radiobutton is checked and a value is entered into the text box', ->
- html = '''
-
-
-
-
-
- '''
- $(html).insertAfter('#input_example_1')
- @problem.checkAnswersAndCheckButton true
- @checkDisabled true
- $('#input_1_1_1').attr('checked', true).trigger('click')
- @checkDisabled true
- $('#input_example_1').val('111').trigger('input')
- @checkDisabled false
- $('#input_1_1_1').attr('checked', false).trigger('click')
- @checkDisabled true
-
describe 'reset', ->
beforeEach ->
@problem = new Problem($('.xblock-student_view'))
diff --git a/common/lib/xmodule/xmodule/js/src/capa/display.coffee b/common/lib/xmodule/xmodule/js/src/capa/display.coffee
index 7ea9ac17a9..d9d1b89c77 100644
--- a/common/lib/xmodule/xmodule/js/src/capa/display.coffee
+++ b/common/lib/xmodule/xmodule/js/src/capa/display.coffee
@@ -49,8 +49,6 @@ class @Problem
window.globalTooltipManager.hide()
@bindResetCorrectness()
- if @checkButton.length
- @checkAnswersAndCheckButton true
# Collapsibles
Collapsible.setCollapsibles(@el)
@@ -454,58 +452,6 @@ class @Problem
element.CodeMirror.save() if element.CodeMirror.save
@answers = @inputs.serialize()
- checkAnswersAndCheckButton: (bind=false) =>
- # Used to check available answers and if something is checked (or the answer is set in some textbox)
- # "Check"/"Final check" button becomes enabled. Otherwise it is disabled by default.
- # params:
- # 'bind' used on the first check to attach event handlers to input fields
- # to change "Check"/"Final check" enable status in case of some manipulations with answers
- answered = true
-
- at_least_one_text_input_found = false
- one_text_input_filled = false
- @el.find("input:text").each (i, text_field) =>
- at_least_one_text_input_found = true
- if $(text_field).is(':visible')
- if $(text_field).val() isnt ''
- one_text_input_filled = true
- if bind
- $(text_field).on 'input', (e) =>
- @checkAnswersAndCheckButton()
- return
- return
- if at_least_one_text_input_found and not one_text_input_filled
- answered = false
-
- @el.find(".choicegroup").each (i, choicegroup_block) =>
- checked = false
- $(choicegroup_block).find("input[type=checkbox], input[type=radio]").each (j, checkbox_or_radio) =>
- if $(checkbox_or_radio).is(':checked')
- checked = true
- if bind
- $(checkbox_or_radio).on 'click', (e) =>
- @checkAnswersAndCheckButton()
- return
- return
- if not checked
- answered = false
- return
-
- @el.find("select").each (i, select_field) =>
- selected_option = $(select_field).find("option:selected").text().trim()
- if selected_option is ''
- answered = false
- if bind
- $(select_field).on 'change', (e) =>
- @checkAnswersAndCheckButton()
- return
- return
-
- if answered
- @enableCheckButton true
- else
- @enableCheckButton false, false
-
bindResetCorrectness: ->
# Loop through all input types
# Bind the reset functions at that scope.
diff --git a/common/test/acceptance/tests/lms/test_problem_types.py b/common/test/acceptance/tests/lms/test_problem_types.py
index 358c0146f3..4ff0d401a5 100644
--- a/common/test/acceptance/tests/lms/test_problem_types.py
+++ b/common/test/acceptance/tests/lms/test_problem_types.py
@@ -6,7 +6,6 @@ See also lettuce tests in lms/djangoapps/courseware/features/problems.feature
import random
import textwrap
-from nose import SkipTest
from abc import ABCMeta, abstractmethod
from nose.plugins.attrib import attr
from selenium.webdriver import ActionChains
@@ -136,8 +135,6 @@ class ProblemTypeTestMixin(object):
"""
Test cases shared amongst problem types.
"""
- can_submit_blank = False
-
@attr('shard_7')
def test_answer_correctly(self):
"""
@@ -203,34 +200,15 @@ class ProblemTypeTestMixin(object):
Then my "" answer is marked "incorrect"
And The "" problem displays a "blank" answer
"""
- if not self.can_submit_blank:
- raise SkipTest("Test incompatible with the current problem type")
-
self.problem_page.wait_for(
lambda: self.problem_page.problem_name == self.problem_name,
"Make sure the correct problem is on the page"
)
+
# Leave the problem unchanged and click check.
- self.assertNotIn('is-disabled', self.problem_page.q(css='div.problem button.check').attrs('class')[0])
self.problem_page.click_check()
self.wait_for_status('incorrect')
- @attr('shard_7')
- def test_cant_submit_blank_answer(self):
- """
- Scenario: I can't submit a blank answer
- When I try to submit blank answer
- Then I can't check a problem
- """
- if self.can_submit_blank:
- raise SkipTest("Test incompatible with the current problem type")
-
- self.problem_page.wait_for(
- lambda: self.problem_page.problem_name == self.problem_name,
- "Make sure the correct problem is on the page"
- )
- self.assertIn('is-disabled', self.problem_page.q(css='div.problem button.check').attrs('class')[0])
-
@attr('a11y')
def test_problem_type_a11y(self):
"""
@@ -258,8 +236,6 @@ class AnnotationProblemTypeTest(ProblemTypeTestBase, ProblemTypeTestMixin):
factory = AnnotationResponseXMLFactory()
- can_submit_blank = True
-
factory_kwargs = {
'title': 'Annotation Problem',
'text': 'The text being annotated',
@@ -710,13 +686,6 @@ class CodeProblemTypeTest(ProblemTypeTestBase, ProblemTypeTestMixin):
"""
pass
- def test_cant_submit_blank_answer(self):
- """
- Overridden for script test because the testing grader always responds
- with "correct"
- """
- pass
-
class ChoiceTextProbelmTypeTestBase(ProblemTypeTestBase):
"""
@@ -832,8 +801,6 @@ class ImageProblemTypeTest(ProblemTypeTestBase, ProblemTypeTestMixin):
factory = ImageResponseXMLFactory()
- can_submit_blank = True
-
factory_kwargs = {
'src': '/static/images/placeholder-image.png',
'rectangle': '(0,0)-(50,50)',
diff --git a/common/test/db_fixtures/sites.json b/common/test/db_fixtures/sites.json
deleted file mode 100644
index 5a7e8bc11b..0000000000
--- a/common/test/db_fixtures/sites.json
+++ /dev/null
@@ -1,20 +0,0 @@
-[
- {
- "pk": 2,
- "model": "sites.Site",
-
- "fields": {
- "domain": "localhost:8003",
- "name": "lms"
- }
- },
- {
- "pk": 3,
- "model": "sites.Site",
-
- "fields": {
- "domain": "localhost:8031",
- "name": "cms"
- }
- }
-]
diff --git a/common/test/test-theme/cms/static/css/.gitignore b/common/test/test-theme/cms/static/css/.gitignore
deleted file mode 100644
index b3a5267117..0000000000
--- a/common/test/test-theme/cms/static/css/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-*.css
diff --git a/common/test/test-theme/cms/static/sass/partials/_variables.scss b/common/test/test-theme/cms/static/sass/partials/_variables.scss
deleted file mode 100644
index 4265a16fec..0000000000
--- a/common/test/test-theme/cms/static/sass/partials/_variables.scss
+++ /dev/null
@@ -1,255 +0,0 @@
-// studio - utilities - variables
-// ====================
-
-// Table of Contents
-// * +Paths
-// * +Grid
-// * +Fonts
-// * +Colors - Utility
-// * +Colors - Primary
-// * +Colors - Shadow
-// * +Color - Application
-// * +Timing
-// * +Archetype UI
-// * +Specific UI
-// * +Deprecated
-
-$baseline: 20px;
-
-// +Paths
-// ====================
-$static-path: '..' !default;
-
-// +Grid
-// ====================
-$gw-column: ($baseline*3);
-$gw-gutter: $baseline;
-$fg-column: $gw-column;
-$fg-gutter: $gw-gutter;
-$fg-max-columns: 12;
-$fg-max-width: 1280px;
-$fg-min-width: 900px;
-
-// +Fonts
-// ====================
-$f-sans-serif: 'Open Sans','Helvetica Neue', Helvetica, Arial, sans-serif;
-$f-monospace: 'Bitstream Vera Sans Mono', Consolas, Courier, monospace;
-
-// +Colors - Utility
-// ====================
-$transparent: rgba(0,0,0,0); // used when color value is needed for UI width/transitions but element is transparent
-
-// +Colors - Primary
-// ====================
-$black: rgb(0,0,0);
-$black-t0: rgba($black, 0.125);
-$black-t1: rgba($black, 0.25);
-$black-t2: rgba($black, 0.5);
-$black-t3: rgba($black, 0.75);
-
-$white: rgb(255,255,255);
-$white-t0: rgba($white, 0.125);
-$white-t1: rgba($white, 0.25);
-$white-t2: rgba($white, 0.5);
-$white-t3: rgba($white, 0.75);
-
-$gray: rgb(127,127,127);
-$gray-l1: tint($gray,20%);
-$gray-l2: tint($gray,40%);
-$gray-l3: tint($gray,60%);
-$gray-l4: tint($gray,80%);
-$gray-l5: tint($gray,90%);
-$gray-l6: tint($gray,95%);
-$gray-l7: tint($gray,99%);
-$gray-d1: shade($gray,20%);
-$gray-d2: shade($gray,40%);
-$gray-d3: shade($gray,60%);
-$gray-d4: shade($gray,80%);
-
-$blue: rgb(0, 159, 230);
-$blue-l1: tint($blue,20%);
-$blue-l2: tint($blue,40%);
-$blue-l3: tint($blue,60%);
-$blue-l4: tint($blue,80%);
-$blue-l5: tint($blue,90%);
-$blue-d1: shade($blue,20%);
-$blue-d2: shade($blue,40%);
-$blue-d3: shade($blue,60%);
-$blue-d4: shade($blue,80%);
-$blue-s1: saturate($blue,15%);
-$blue-s2: saturate($blue,30%);
-$blue-s3: saturate($blue,45%);
-$blue-u1: desaturate($blue,15%);
-$blue-u2: desaturate($blue,30%);
-$blue-u3: desaturate($blue,45%);
-$blue-t0: rgba($blue, 0.125);
-$blue-t1: rgba($blue, 0.25);
-$blue-t2: rgba($blue, 0.50);
-$blue-t3: rgba($blue, 0.75);
-
-$pink: rgb(183, 37, 103); // #b72567;
-$pink-l1: tint($pink,20%);
-$pink-l2: tint($pink,40%);
-$pink-l3: tint($pink,60%);
-$pink-l4: tint($pink,80%);
-$pink-l5: tint($pink,90%);
-$pink-d1: shade($pink,20%);
-$pink-d2: shade($pink,40%);
-$pink-d3: shade($pink,60%);
-$pink-d4: shade($pink,80%);
-$pink-s1: saturate($pink,15%);
-$pink-s2: saturate($pink,30%);
-$pink-s3: saturate($pink,45%);
-$pink-u1: desaturate($pink,15%);
-$pink-u2: desaturate($pink,30%);
-$pink-u3: desaturate($pink,45%);
-
-$red: rgb(178, 6, 16); // #b20610;
-$red-l1: tint($red,20%);
-$red-l2: tint($red,40%);
-$red-l3: tint($red,60%);
-$red-l4: tint($red,80%);
-$red-l5: tint($red,90%);
-$red-d1: shade($red,20%);
-$red-d2: shade($red,40%);
-$red-d3: shade($red,60%);
-$red-d4: shade($red,80%);
-$red-s1: saturate($red,15%);
-$red-s2: saturate($red,30%);
-$red-s3: saturate($red,45%);
-$red-u1: desaturate($red,15%);
-$red-u2: desaturate($red,30%);
-$red-u3: desaturate($red,45%);
-
-$green: rgb(37, 184, 90); // #25b85a
-$green-l1: tint($green,20%);
-$green-l2: tint($green,40%);
-$green-l3: tint($green,60%);
-$green-l4: tint($green,80%);
-$green-l5: tint($green,90%);
-$green-d1: shade($green,20%);
-$green-d2: shade($green,40%);
-$green-d3: shade($green,60%);
-$green-d4: shade($green,80%);
-$green-s1: saturate($green,15%);
-$green-s2: saturate($green,30%);
-$green-s3: saturate($green,45%);
-$green-u1: desaturate($green,15%);
-$green-u2: desaturate($green,30%);
-$green-u3: desaturate($green,45%);
-
-$yellow: rgb(237, 189, 60);
-$yellow-l1: tint($yellow,20%);
-$yellow-l2: tint($yellow,40%);
-$yellow-l3: tint($yellow,60%);
-$yellow-l4: tint($yellow,80%);
-$yellow-l5: tint($yellow,90%);
-$yellow-d1: shade($yellow,20%);
-$yellow-d2: shade($yellow,40%);
-$yellow-d3: shade($yellow,60%);
-$yellow-d4: shade($yellow,80%);
-$yellow-s1: saturate($yellow,15%);
-$yellow-s2: saturate($yellow,30%);
-$yellow-s3: saturate($yellow,45%);
-$yellow-u1: desaturate($yellow,15%);
-$yellow-u2: desaturate($yellow,30%);
-$yellow-u3: desaturate($yellow,45%);
-
-$orange: rgb(237, 189, 60);
-$orange-l1: tint($orange,20%);
-$orange-l2: tint($orange,40%);
-$orange-l3: tint($orange,60%);
-$orange-l4: tint($orange,80%);
-$orange-l5: tint($orange,90%);
-$orange-d1: shade($orange,20%);
-$orange-d2: shade($orange,40%);
-$orange-d3: shade($orange,60%);
-$orange-d4: shade($orange,80%);
-$orange-s1: saturate($orange,15%);
-$orange-s2: saturate($orange,30%);
-$orange-s3: saturate($orange,45%);
-$orange-u1: desaturate($orange,15%);
-$orange-u2: desaturate($orange,30%);
-$orange-u3: desaturate($orange,45%);
-
-// +Colors - Shadows
-// ====================
-$shadow: rgba($black, 0.2);
-$shadow-l1: rgba($black, 0.1);
-$shadow-l2: rgba($black, 0.05);
-$shadow-d1: rgba($black, 0.4);
-$shadow-d2: rgba($black, 0.6);
-
-// +Colors - Application
-// ====================
-$color-draft: $gray-l3;
-$color-live: $blue;
-$color-ready: $green;
-$color-warning: $orange-l2;
-$color-error: $red-l2;
-$color-staff-only: $black;
-$color-gated: $black;
-$color-visibility-set: $black;
-
-$color-heading-base: $gray-d2;
-$color-copy-base: $gray-l1;
-$color-copy-emphasized: $gray-d2;
-
-// +Timing
-// ====================
-// used for animation/transition mixin syncing
-$tmg-s3: 3.0s;
-$tmg-s2: 2.0s;
-$tmg-s1: 1.0s;
-$tmg-avg: 0.75s;
-$tmg-f1: 0.50s;
-$tmg-f2: 0.25s;
-$tmg-f3: 0.125s;
-
-// +Archetype UI
-// ====================
-$ui-action-primary-color: $blue-u2;
-$ui-action-primary-color-focus: $blue-s1;
-
-$ui-link-color: $blue-u2;
-$ui-link-color-focus: $blue-s1;
-
-// +Specific UI
-// ====================
-$ui-notification-height: ($baseline*10);
-$ui-update-color: $blue-l4;
-
-// +Deprecated
-// ====================
-// do not use, future clean up will use updated styles
-$baseFontColor: $gray-d2;
-$lighter-base-font-color: rgb(100,100,100);
-$offBlack: #3c3c3c;
-$green: #108614;
-$lightGrey: #edf1f5;
-$mediumGrey: #b0b6c2;
-$darkGrey: #8891a1;
-$extraDarkGrey: #3d4043;
-$paleYellow: #fffcf1;
-$yellow: rgb(255, 254, 223);
-$green: rgb(37, 184, 90);
-$brightGreen: rgb(22, 202, 87);
-$disabledGreen: rgb(124, 206, 153);
-$darkGreen: rgb(52, 133, 76);
-
-// These colors are updated for testing purposes
-$lightBluishGrey: rgb(0, 250, 0);
-$lightBluishGrey2: rgb(0, 250, 0);
-$error-red: rgb(253, 87, 87);
-
-
-//carryover from LMS for xmodules
-$sidebar-color: rgb(246, 246, 246);
-
-// type
-$sans-serif: $f-sans-serif;
-$body-line-height: golden-ratio(.875em, 1);
-
-// carried over from LMS for xmodules
-$action-primary-active-bg: #1AA1DE; // $m-blue
-$very-light-text: $white;
diff --git a/common/test/test-theme/cms/templates/login.html b/common/test/test-theme/cms/templates/login.html
deleted file mode 100644
index 30757bfe11..0000000000
--- a/common/test/test-theme/cms/templates/login.html
+++ /dev/null
@@ -1,55 +0,0 @@
-<%inherit file="base.html" />
-<%def name="online_help_token()"><% return "login" %>%def>
-<%!
-from django.core.urlresolvers import reverse
-from django.utils.translation import ugettext as _
-%>
-<%block name="title">${_("Sign In")}%block>
-<%block name="bodyclass">not-signedin view-signin%block>
-
-<%block name="content">
-
-
-
-
${_("Sign In to {studio_name}").format(studio_name=settings.STUDIO_NAME)}
-%block>
-
-<%block name="requirejs">
- require(["js/factories/login"], function(LoginFactory) {
- LoginFactory("${reverse('homepage')}");
- });
-%block>
diff --git a/common/test/test-theme/lms/static/css/.gitignore b/common/test/test-theme/lms/static/css/.gitignore
deleted file mode 100644
index b3a5267117..0000000000
--- a/common/test/test-theme/lms/static/css/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-*.css
diff --git a/common/test/test-theme/lms/static/images/logo.png b/common/test/test-theme/lms/static/images/logo.png
deleted file mode 100644
index 5efc6b63a4..0000000000
Binary files a/common/test/test-theme/lms/static/images/logo.png and /dev/null differ
diff --git a/common/test/test-theme/lms/static/sass/partials/base/_variables.scss b/common/test/test-theme/lms/static/sass/partials/base/_variables.scss
deleted file mode 100755
index 43f66799a0..0000000000
--- a/common/test/test-theme/lms/static/sass/partials/base/_variables.scss
+++ /dev/null
@@ -1,5 +0,0 @@
-@import 'lms/static/sass/partials/base/variables';
-
-$header-bg: rgb(0,250,0);
-$footer-bg: rgb(0,250,0);
-$container-bg: rgb(0,250,0);
diff --git a/common/test/test-theme/lms/templates/footer.html b/common/test/test-theme/lms/templates/footer.html
deleted file mode 100644
index c9e73c0117..0000000000
--- a/common/test/test-theme/lms/templates/footer.html
+++ /dev/null
@@ -1,10 +0,0 @@
-
diff --git a/lms/djangoapps/branding/tests/test_views.py b/lms/djangoapps/branding/tests/test_views.py
index c6d4ee8340..a6cb5d7249 100644
--- a/lms/djangoapps/branding/tests/test_views.py
+++ b/lms/djangoapps/branding/tests/test_views.py
@@ -10,7 +10,7 @@ import mock
import ddt
from config_models.models import cache
from branding.models import BrandingApiConfig
-from openedx.core.djangoapps.theming.test_util import with_comprehensive_theme_context
+from openedx.core.djangoapps.theming.test_util import with_edx_domain_context
@ddt.ddt
@@ -30,19 +30,19 @@ class TestFooter(TestCase):
@ddt.data(
# Open source version
- (None, "application/json", "application/json; charset=utf-8", "Open edX"),
- (None, "text/html", "text/html; charset=utf-8", "lms-footer.css"),
- (None, "text/html", "text/html; charset=utf-8", "Open edX"),
+ (False, "application/json", "application/json; charset=utf-8", "Open edX"),
+ (False, "text/html", "text/html; charset=utf-8", "lms-footer.css"),
+ (False, "text/html", "text/html; charset=utf-8", "Open edX"),
# EdX.org version
- ("edx.org", "application/json", "application/json; charset=utf-8", "edX Inc"),
- ("edx.org", "text/html", "text/html; charset=utf-8", "lms-footer-edx.css"),
- ("edx.org", "text/html", "text/html; charset=utf-8", "edX Inc"),
+ (True, "application/json", "application/json; charset=utf-8", "edX Inc"),
+ (True, "text/html", "text/html; charset=utf-8", "lms-footer-edx.css"),
+ (True, "text/html", "text/html; charset=utf-8", "edX Inc"),
)
@ddt.unpack
- def test_footer_content_types(self, theme, accepts, content_type, content):
+ def test_footer_content_types(self, is_edx_domain, accepts, content_type, content):
self._set_feature_flag(True)
- with with_comprehensive_theme_context(theme):
+ with with_edx_domain_context(is_edx_domain):
resp = self._get_footer(accepts=accepts)
self.assertEqual(resp.status_code, 200)
@@ -50,10 +50,10 @@ class TestFooter(TestCase):
self.assertIn(content, resp.content)
@mock.patch.dict(settings.FEATURES, {'ENABLE_FOOTER_MOBILE_APP_LINKS': True})
- @ddt.data("edx.org", None)
- def test_footer_json(self, theme):
+ @ddt.data(True, False)
+ def test_footer_json(self, is_edx_domain):
self._set_feature_flag(True)
- with with_comprehensive_theme_context(theme):
+ with with_edx_domain_context(is_edx_domain):
resp = self._get_footer()
self.assertEqual(resp.status_code, 200)
@@ -142,18 +142,18 @@ class TestFooter(TestCase):
@ddt.data(
# OpenEdX
- (None, "en", "lms-footer.css"),
- (None, "ar", "lms-footer-rtl.css"),
+ (False, "en", "lms-footer.css"),
+ (False, "ar", "lms-footer-rtl.css"),
# EdX.org
- ("edx.org", "en", "lms-footer-edx.css"),
- ("edx.org", "ar", "lms-footer-edx-rtl.css"),
+ (True, "en", "lms-footer-edx.css"),
+ (True, "ar", "lms-footer-edx-rtl.css"),
)
@ddt.unpack
- def test_language_rtl(self, theme, language, static_path):
+ def test_language_rtl(self, is_edx_domain, language, static_path):
self._set_feature_flag(True)
- with with_comprehensive_theme_context(theme):
+ with with_edx_domain_context(is_edx_domain):
resp = self._get_footer(accepts="text/html", params={'language': language})
self.assertEqual(resp.status_code, 200)
@@ -161,18 +161,18 @@ class TestFooter(TestCase):
@ddt.data(
# OpenEdX
- (None, True),
- (None, False),
+ (False, True),
+ (False, False),
# EdX.org
- ("edx.org", True),
- ("edx.org", False),
+ (True, True),
+ (True, False),
)
@ddt.unpack
- def test_show_openedx_logo(self, theme, show_logo):
+ def test_show_openedx_logo(self, is_edx_domain, show_logo):
self._set_feature_flag(True)
- with with_comprehensive_theme_context(theme):
+ with with_edx_domain_context(is_edx_domain):
params = {'show-openedx-logo': 1} if show_logo else {}
resp = self._get_footer(accepts="text/html", params=params)
@@ -185,17 +185,17 @@ class TestFooter(TestCase):
@ddt.data(
# OpenEdX
- (None, False),
- (None, True),
+ (False, False),
+ (False, True),
# EdX.org
- ("edx.org", False),
- ("edx.org", True),
+ (True, False),
+ (True, True),
)
@ddt.unpack
- def test_include_dependencies(self, theme, include_dependencies):
+ def test_include_dependencies(self, is_edx_domain, include_dependencies):
self._set_feature_flag(True)
- with with_comprehensive_theme_context(theme):
+ with with_edx_domain_context(is_edx_domain):
params = {'include-dependencies': 1} if include_dependencies else {}
resp = self._get_footer(accepts="text/html", params=params)
diff --git a/lms/djangoapps/ccx/migrations/0003_add_master_course_staff_in_ccx.py b/lms/djangoapps/ccx/migrations/0003_add_master_course_staff_in_ccx.py
index c97449a77b..977a69b5b8 100644
--- a/lms/djangoapps/ccx/migrations/0003_add_master_course_staff_in_ccx.py
+++ b/lms/djangoapps/ccx/migrations/0003_add_master_course_staff_in_ccx.py
@@ -1,15 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
+import logging
+
from ccx_keys.locator import CCXLocator
from courseware.courses import get_course_by_id
from django.db import migrations
+from django.http import Http404
from lms.djangoapps.ccx.utils import (
add_master_course_staff_to_ccx,
remove_master_course_staff_from_ccx,
)
+log = logging.getLogger("edx.ccx")
+
def add_master_course_staff_to_ccx_for_existing_ccx(apps, schema_editor):
"""
@@ -23,16 +28,24 @@ def add_master_course_staff_to_ccx_for_existing_ccx(apps, schema_editor):
CustomCourseForEdX = apps.get_model("ccx", "CustomCourseForEdX")
list_ccx = CustomCourseForEdX.objects.all()
for ccx in list_ccx:
- if ccx.course_id.deprecated:
- # prevent migration for deprecated course ids.
+ if not ccx.course_id or ccx.course_id.deprecated:
+ # prevent migration for deprecated course ids or invalid ids.
continue
ccx_locator = CCXLocator.from_course_locator(ccx.course_id, unicode(ccx.id))
- add_master_course_staff_to_ccx(
- get_course_by_id(ccx.course_id),
- ccx_locator,
- ccx.display_name,
- send_email=False
- )
+ try:
+ course = get_course_by_id(ccx.course_id)
+ add_master_course_staff_to_ccx(
+ course,
+ ccx_locator,
+ ccx.display_name,
+ send_email=False
+ )
+ except Http404:
+ log.warning(
+ "Unable to add instructors and staff of master course %s to ccx %s.",
+ ccx.course_id,
+ ccx_locator
+ )
def remove_master_course_staff_from_ccx_for_existing_ccx(apps, schema_editor):
@@ -47,17 +60,24 @@ def remove_master_course_staff_from_ccx_for_existing_ccx(apps, schema_editor):
CustomCourseForEdX = apps.get_model("ccx", "CustomCourseForEdX")
list_ccx = CustomCourseForEdX.objects.all()
for ccx in list_ccx:
- if ccx.course_id.deprecated:
- # prevent migration for deprecated course ids.
+ if not ccx.course_id or ccx.course_id.deprecated:
+ # prevent migration for deprecated course ids or invalid ids.
continue
ccx_locator = CCXLocator.from_course_locator(ccx.course_id, unicode(ccx.id))
- remove_master_course_staff_from_ccx(
- get_course_by_id(ccx.course_id),
- ccx_locator,
- ccx.display_name,
- send_email=False
- )
-
+ try:
+ course = get_course_by_id(ccx.course_id)
+ remove_master_course_staff_from_ccx(
+ course,
+ ccx_locator,
+ ccx.display_name,
+ send_email=False
+ )
+ except Http404:
+ log.warning(
+ "Unable to remove instructors and staff of master course %s from ccx %s.",
+ ccx.course_id,
+ ccx_locator
+ )
class Migration(migrations.Migration):
@@ -65,6 +85,7 @@ class Migration(migrations.Migration):
('ccx', '0001_initial'),
('ccx', '0002_customcourseforedx_structure_json'),
('course_overviews','0010_auto_20160329_2317'), # because we use course overview and are in the same release as that table is modified
+ ('verified_track_content','0001_initial'), # because we use enrollement code and are in the same release as an enrollement related table is created
]
operations = [
diff --git a/lms/djangoapps/commerce/tests/test_views.py b/lms/djangoapps/commerce/tests/test_views.py
index c3df4e0a41..703a518c12 100644
--- a/lms/djangoapps/commerce/tests/test_views.py
+++ b/lms/djangoapps/commerce/tests/test_views.py
@@ -8,7 +8,7 @@ from django.test import TestCase
import mock
from student.tests.factories import UserFactory
-from openedx.core.djangoapps.theming.test_util import with_comprehensive_theme
+from openedx.core.djangoapps.theming.test_util import with_is_edx_domain
class UserMixin(object):
@@ -85,7 +85,7 @@ class ReceiptViewTests(UserMixin, TestCase):
self.assertRegexpMatches(response.content, user_message if is_user_message_expected else system_message)
self.assertNotRegexpMatches(response.content, user_message if not is_user_message_expected else system_message)
- @with_comprehensive_theme("edx.org")
+ @with_is_edx_domain(True)
def test_hide_nav_header(self):
self._login()
post_data = {'decision': 'ACCEPT', 'reason_code': '200', 'signed_field_names': 'dummy'}
diff --git a/lms/djangoapps/course_wiki/tests/test_comprehensive_theming.py b/lms/djangoapps/course_wiki/tests/test_comprehensive_theming.py
index 3042809e6d..f763510efb 100644
--- a/lms/djangoapps/course_wiki/tests/test_comprehensive_theming.py
+++ b/lms/djangoapps/course_wiki/tests/test_comprehensive_theming.py
@@ -1,6 +1,7 @@
"""
Tests for wiki middleware.
"""
+from django.conf import settings
from django.test.client import Client
from nose.plugins.attrib import attr
from wiki.models import URLPath
@@ -32,7 +33,7 @@ class TestComprehensiveTheming(ModuleStoreTestCase):
self.client = Client()
self.client.login(username='instructor', password='secret')
- @with_comprehensive_theme('red-theme')
+ @with_comprehensive_theme(settings.REPO_ROOT / 'themes/red-theme')
def test_themed_footer(self):
"""
Tests that theme footer is used rather than standard
diff --git a/lms/djangoapps/course_wiki/views.py b/lms/djangoapps/course_wiki/views.py
index 1913454e8c..92372e828e 100644
--- a/lms/djangoapps/course_wiki/views.py
+++ b/lms/djangoapps/course_wiki/views.py
@@ -6,6 +6,8 @@ import re
import cgi
from django.conf import settings
+from django.contrib.sites.models import Site
+from django.core.exceptions import ImproperlyConfigured
from django.shortcuts import redirect
from django.utils.translation import ugettext as _
@@ -48,6 +50,21 @@ def course_wiki_redirect(request, course_id): # pylint: disable=unused-argument
if not valid_slug:
return redirect("wiki:get", path="")
+ # The wiki needs a Site object created. We make sure it exists here
+ try:
+ Site.objects.get_current()
+ except Site.DoesNotExist:
+ new_site = Site()
+ new_site.domain = settings.SITE_NAME
+ new_site.name = "edX"
+ new_site.save()
+ site_id = str(new_site.id)
+ if site_id != str(settings.SITE_ID):
+ msg = "No site object was created and the SITE_ID doesn't match the newly created one. {} != {}".format(
+ site_id, settings.SITE_ID
+ )
+ raise ImproperlyConfigured(msg)
+
try:
urlpath = URLPath.get_by_path(course_slug, select_related=True)
diff --git a/lms/djangoapps/courseware/features/problems.feature b/lms/djangoapps/courseware/features/problems.feature
index 5994fcc464..153b20aa7b 100644
--- a/lms/djangoapps/courseware/features/problems.feature
+++ b/lms/djangoapps/courseware/features/problems.feature
@@ -180,22 +180,16 @@ Feature: LMS.Answer problems
Examples:
| ProblemType | Points Possible |
+ | drop down | 1 point possible |
+ | multiple choice | 1 point possible |
+ | checkbox | 1 point possible |
+ | radio | 1 point possible |
+ #| string | 1 point possible |
+ | numerical | 1 point possible |
+ | formula | 1 point possible |
+ | script | 2 points possible |
| image | 1 point possible |
- Scenario: I can't submit a blank answer
- Given I am viewing a "" problem
- Then I can't check a problem
-
- Examples:
- | ProblemType |
- | drop down |
- | multiple choice |
- | checkbox |
- | radio |
- | string |
- | numerical |
- | formula |
- | script |
Scenario: I can reset the correctness of a problem after changing my answer
Given I am viewing a "" problem
@@ -240,3 +234,21 @@ Feature: LMS.Answer problems
| multiple choice | incorrect | correct |
| radio | correct | incorrect |
| radio | incorrect | correct |
+
+
+ Scenario: I can reset the correctness of a problem after submitting a blank answer
+ Given I am viewing a "" problem
+ When I check a problem
+ And I input an answer on a "" problem "correctly"
+ Then my "" answer is marked "unanswered"
+
+ Examples:
+ | ProblemType |
+ | drop down |
+ | multiple choice |
+ | checkbox |
+ | radio |
+ #| string |
+ | numerical |
+ | formula |
+ | script |
diff --git a/lms/djangoapps/courseware/features/problems.py b/lms/djangoapps/courseware/features/problems.py
index 559a90e0af..730997b0a1 100644
--- a/lms/djangoapps/courseware/features/problems.py
+++ b/lms/djangoapps/courseware/features/problems.py
@@ -92,21 +92,12 @@ def check_problem(step):
# first scroll down so the loading mathjax button does not
# cover up the Check button
world.browser.execute_script("window.scrollTo(0,1024)")
- assert world.is_css_not_present("button.check.is-disabled")
world.css_click("button.check")
# Wait for the problem to finish re-rendering
world.wait_for_ajax_complete()
-@step(u"I can't check a problem")
-def assert_cant_check_problem(step): # pylint: disable=unused-argument
- # first scroll down so the loading mathjax button does not
- # cover up the Check button
- world.browser.execute_script("window.scrollTo(0,1024)")
- assert world.is_css_present("button.check.is-disabled")
-
-
@step(u'The "([^"]*)" problem displays a "([^"]*)" answer')
def assert_problem_has_answer(step, problem_type, answer_class):
'''
diff --git a/lms/djangoapps/courseware/tests/test_comprehensive_theming.py b/lms/djangoapps/courseware/tests/test_comprehensive_theming.py
index dcc8d10485..752eb81030 100644
--- a/lms/djangoapps/courseware/tests/test_comprehensive_theming.py
+++ b/lms/djangoapps/courseware/tests/test_comprehensive_theming.py
@@ -6,33 +6,21 @@ from django.test import TestCase
from path import path # pylint: disable=no-name-in-module
from django.contrib import staticfiles
-from paver.easy import call_task
-
from openedx.core.djangoapps.theming.test_util import with_comprehensive_theme
-from openedx.core.lib.tempdir import mkdtemp_clean, create_symlink, delete_symlink
+from openedx.core.lib.tempdir import mkdtemp_clean
class TestComprehensiveTheming(TestCase):
"""Test comprehensive theming."""
- @classmethod
- def setUpClass(cls):
- compile_sass('lms')
- super(TestComprehensiveTheming, cls).setUpClass()
-
def setUp(self):
super(TestComprehensiveTheming, self).setUp()
# Clear the internal staticfiles caches, to get test isolation.
staticfiles.finders.get_finder.cache_clear()
- @with_comprehensive_theme('red-theme')
+ @with_comprehensive_theme(settings.REPO_ROOT / 'themes/red-theme')
def test_red_footer(self):
- """
- Tests templates from theme are rendered if available.
- `red-theme` has header.html and footer.html so this test
- asserts presence of the content from header.html and footer.html
- """
resp = self.client.get('/')
self.assertEqual(resp.status_code, 200)
# This string comes from footer.html
@@ -46,16 +34,12 @@ class TestComprehensiveTheming(TestCase):
# of test.
# Make a temp directory as a theme.
- themes_dir = path(mkdtemp_clean())
- tmp_theme = "temp_theme"
- template_dir = themes_dir / tmp_theme / "lms/templates"
+ tmp_theme = path(mkdtemp_clean())
+ template_dir = tmp_theme / "lms/templates"
template_dir.makedirs()
with open(template_dir / "footer.html", "w") as footer:
footer.write("")
- dest_path = path(settings.COMPREHENSIVE_THEME_DIR) / tmp_theme
- create_symlink(themes_dir / tmp_theme, dest_path)
-
@with_comprehensive_theme(tmp_theme)
def do_the_test(self):
"""A function to do the work so we can use the decorator."""
@@ -64,22 +48,18 @@ class TestComprehensiveTheming(TestCase):
self.assertContains(resp, "TEMPORARY THEME")
do_the_test(self)
- # remove symlinks before running subsequent tests
- delete_symlink(dest_path)
def test_theme_adjusts_staticfiles_search_path(self):
- """
- Tests theme directories are added to staticfiles search path.
- """
+ # Test that a theme adds itself to the staticfiles search path.
before_finders = list(settings.STATICFILES_FINDERS)
before_dirs = list(settings.STATICFILES_DIRS)
- @with_comprehensive_theme('red-theme')
+ @with_comprehensive_theme(settings.REPO_ROOT / 'themes/red-theme')
def do_the_test(self):
"""A function to do the work so we can use the decorator."""
self.assertEqual(list(settings.STATICFILES_FINDERS), before_finders)
- self.assertIn(settings.REPO_ROOT / 'themes/red-theme/lms/static', settings.STATICFILES_DIRS)
- self.assertEqual(settings.STATICFILES_DIRS, before_dirs)
+ self.assertEqual(settings.STATICFILES_DIRS[0], settings.REPO_ROOT / 'themes/red-theme/lms/static')
+ self.assertEqual(settings.STATICFILES_DIRS[1:], before_dirs)
do_the_test(self)
@@ -87,9 +67,9 @@ class TestComprehensiveTheming(TestCase):
result = staticfiles.finders.find('images/logo.png')
self.assertEqual(result, settings.REPO_ROOT / 'lms/static/images/logo.png')
- @with_comprehensive_theme('red-theme')
+ @with_comprehensive_theme(settings.REPO_ROOT / 'themes/red-theme')
def test_overridden_logo_image(self):
- result = staticfiles.finders.find('red-theme/images/logo.png')
+ result = staticfiles.finders.find('images/logo.png')
self.assertEqual(result, settings.REPO_ROOT / 'themes/red-theme/lms/static/images/logo.png')
def test_default_favicon(self):
@@ -99,54 +79,10 @@ class TestComprehensiveTheming(TestCase):
result = staticfiles.finders.find('images/favicon.ico')
self.assertEqual(result, settings.REPO_ROOT / 'lms/static/images/favicon.ico')
- @with_comprehensive_theme('red-theme')
- def test_css(self):
- """
- Test that static files finders are adjusted according to the applied comprehensive theme.
- """
- result = staticfiles.finders.find('red-theme/css/lms-main-v1.css')
- self.assertEqual(result, settings.REPO_ROOT / "themes/red-theme/lms/static/css/lms-main-v1.css")
-
- lms_main_css = ""
- with open(result) as css_file:
- lms_main_css += css_file.read()
-
- self.assertIn("background:#fa0000", lms_main_css)
-
- def test_default_css(self):
- """
- Test default css is served if no theme is applied
- """
- result = staticfiles.finders.find('css/lms-main-v1.css')
- self.assertEqual(result, settings.REPO_ROOT / "lms/static/css/lms-main-v1.css")
-
- lms_main_css = ""
- with open(result) as css_file:
- lms_main_css += css_file.read()
-
- self.assertNotIn("background:#00fa00", lms_main_css)
-
- @with_comprehensive_theme('red-theme')
+ @with_comprehensive_theme(settings.REPO_ROOT / 'themes/red-theme')
def test_overridden_favicon(self):
"""
Test comprehensive theme override on favicon image.
"""
- result = staticfiles.finders.find('red-theme/images/favicon.ico')
+ result = staticfiles.finders.find('images/favicon.ico')
self.assertEqual(result, settings.REPO_ROOT / 'themes/red-theme/lms/static/images/favicon.ico')
-
-
-def compile_sass(system):
- """
- Process xmodule assets and compile sass files.
-
- :param system - 'lms' or 'cms', specified the system to compile sass for.
- """
- # Compile system sass files
- call_task(
- 'pavelib.assets.update_assets',
- args=(
- system,
- "--themes_dir={themes_dir}".format(themes_dir=settings.COMPREHENSIVE_THEME_DIR),
- "--themes=red-theme",
- "--settings=test"),
- )
diff --git a/lms/djangoapps/courseware/tests/test_course_info.py b/lms/djangoapps/courseware/tests/test_course_info.py
index f71db15908..952e1bc21f 100644
--- a/lms/djangoapps/courseware/tests/test_course_info.py
+++ b/lms/djangoapps/courseware/tests/test_course_info.py
@@ -265,8 +265,7 @@ class SelfPacedCourseInfoTestCase(LoginEnrollmentTestCase, SharedModuleStoreTest
url = reverse('info', args=[unicode(course.id)])
with self.assertNumQueries(sql_queries):
with check_mongo_calls(mongo_queries):
- with mock.patch("openedx.core.djangoapps.theming.helpers.get_current_site", return_value=None):
- resp = self.client.get(url)
+ resp = self.client.get(url)
self.assertEqual(resp.status_code, 200)
def test_num_queries_instructor_paced(self):
diff --git a/lms/djangoapps/courseware/tests/test_footer.py b/lms/djangoapps/courseware/tests/test_footer.py
index c1db2a77be..0a14ee1860 100644
--- a/lms/djangoapps/courseware/tests/test_footer.py
+++ b/lms/djangoapps/courseware/tests/test_footer.py
@@ -9,7 +9,7 @@ from django.conf import settings
from django.test import TestCase
from django.test.utils import override_settings
-from openedx.core.djangoapps.theming.test_util import with_comprehensive_theme
+from openedx.core.djangoapps.theming.test_util import with_is_edx_domain
@attr('shard_1')
@@ -37,7 +37,7 @@ class TestFooter(TestCase):
"youtube": "https://www.youtube.com/"
}
- @with_comprehensive_theme("edx.org")
+ @with_is_edx_domain(True)
def test_edx_footer(self):
"""
Verify that the homepage, when accessed at edx.org, has the edX footer
@@ -46,6 +46,7 @@ class TestFooter(TestCase):
self.assertEqual(resp.status_code, 200)
self.assertContains(resp, 'footer-edx-v3')
+ @with_is_edx_domain(False)
def test_openedx_footer(self):
"""
Verify that the homepage, when accessed at something other than
@@ -55,7 +56,7 @@ class TestFooter(TestCase):
self.assertEqual(resp.status_code, 200)
self.assertContains(resp, 'footer-openedx')
- @with_comprehensive_theme("edx.org")
+ @with_is_edx_domain(True)
@override_settings(
SOCIAL_MEDIA_FOOTER_NAMES=SOCIAL_MEDIA_NAMES,
SOCIAL_MEDIA_FOOTER_URLS=SOCIAL_MEDIA_URLS
diff --git a/lms/djangoapps/student_account/test/test_views.py b/lms/djangoapps/student_account/test/test_views.py
index 38ce17f797..59dad97124 100644
--- a/lms/djangoapps/student_account/test/test_views.py
+++ b/lms/djangoapps/student_account/test/test_views.py
@@ -26,7 +26,7 @@ from student_account.views import account_settings_context
from third_party_auth.tests.testutil import simulate_running_pipeline, ThirdPartyAuthTestMixin
from util.testing import UrlResetMixin
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
-from openedx.core.djangoapps.theming.test_util import with_comprehensive_theme_context
+from openedx.core.djangoapps.theming.test_util import with_edx_domain_context
@ddt.ddt
@@ -247,13 +247,13 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi
self.assertRedirects(response, reverse("dashboard"))
@ddt.data(
- (None, "signin_user"),
- (None, "register_user"),
- ("edx.org", "signin_user"),
- ("edx.org", "register_user"),
+ (False, "signin_user"),
+ (False, "register_user"),
+ (True, "signin_user"),
+ (True, "register_user"),
)
@ddt.unpack
- def test_login_and_registration_form_signin_preserves_params(self, theme, url_name):
+ def test_login_and_registration_form_signin_preserves_params(self, is_edx_domain, url_name):
params = [
('course_id', 'edX/DemoX/Demo_Course'),
('enrollment_action', 'enroll'),
@@ -261,7 +261,7 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi
# The response should have a "Sign In" button with the URL
# that preserves the querystring params
- with with_comprehensive_theme_context(theme):
+ with with_edx_domain_context(is_edx_domain):
response = self.client.get(reverse(url_name), params)
expected_url = '/login?{}'.format(self._finish_auth_url_param(params + [('next', '/dashboard')]))
@@ -277,7 +277,7 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi
]
# Verify that this parameter is also preserved
- with with_comprehensive_theme_context(theme):
+ with with_edx_domain_context(is_edx_domain):
response = self.client.get(reverse(url_name), params)
expected_url = '/login?{}'.format(self._finish_auth_url_param(params))
diff --git a/lms/djangoapps/verify_student/tests/test_views.py b/lms/djangoapps/verify_student/tests/test_views.py
index f21c5696f2..e1405a58fd 100644
--- a/lms/djangoapps/verify_student/tests/test_views.py
+++ b/lms/djangoapps/verify_student/tests/test_views.py
@@ -39,7 +39,7 @@ from commerce.models import CommerceConfiguration
from commerce.tests import TEST_PAYMENT_DATA, TEST_API_URL, TEST_API_SIGNING_KEY, TEST_PUBLIC_URL_ROOT
from embargo.test_utils import restrict_course
from openedx.core.djangoapps.user_api.accounts.api import get_account_settings
-from openedx.core.djangoapps.theming.test_util import with_comprehensive_theme
+from openedx.core.djangoapps.theming.test_util import with_is_edx_domain
from shoppingcart.models import Order, CertificateItem
from student.tests.factories import UserFactory, CourseEnrollmentFactory
from student.models import CourseEnrollment
@@ -319,7 +319,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
)
self._assert_redirects_to_dashboard(response)
- @with_comprehensive_theme("edx.org")
+ @with_is_edx_domain(True)
@ddt.data("verify_student_start_flow", "verify_student_begin_flow")
def test_pay_and_verify_hides_header_nav(self, payment_flow):
course = self._create_course("verified")
diff --git a/lms/envs/aws.py b/lms/envs/aws.py
index b81ba6772c..0f2dc51f6b 100644
--- a/lms/envs/aws.py
+++ b/lms/envs/aws.py
@@ -243,7 +243,6 @@ BULK_EMAIL_ROUTING_KEY_SMALL_JOBS = LOW_PRIORITY_QUEUE
# Theme overrides
THEME_NAME = ENV_TOKENS.get('THEME_NAME', None)
COMPREHENSIVE_THEME_DIR = path(ENV_TOKENS.get('COMPREHENSIVE_THEME_DIR', COMPREHENSIVE_THEME_DIR))
-THEME_CACHE_TIMEOUT = ENV_TOKENS.get('THEME_CACHE_TIMEOUT', THEME_CACHE_TIMEOUT)
# Marketing link overrides
MKTG_URL_LINK_MAP.update(ENV_TOKENS.get('MKTG_URL_LINK_MAP', {}))
@@ -445,6 +444,7 @@ AWS_STORAGE_BUCKET_NAME = AUTH_TOKENS.get('AWS_STORAGE_BUCKET_NAME', 'edxuploads
# Disabling querystring auth instructs Boto to exclude the querystring parameters (e.g. signature, access key) it
# normally appends to every returned URL.
AWS_QUERYSTRING_AUTH = AUTH_TOKENS.get('AWS_QUERYSTRING_AUTH', True)
+AWS_S3_CUSTOM_DOMAIN = AUTH_TOKENS.get('AWS_S3_CUSTOM_DOMAIN', 'edxuploads.s3.amazonaws.com')
if AUTH_TOKENS.get('DEFAULT_FILE_STORAGE'):
DEFAULT_FILE_STORAGE = AUTH_TOKENS.get('DEFAULT_FILE_STORAGE')
diff --git a/lms/envs/bok_choy.py b/lms/envs/bok_choy.py
index c0d801a556..91f3a4e1d6 100644
--- a/lms/envs/bok_choy.py
+++ b/lms/envs/bok_choy.py
@@ -61,9 +61,9 @@ STATIC_URL = "/static/"
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
)
-STATICFILES_DIRS = [
+STATICFILES_DIRS = (
(TEST_ROOT / "staticfiles" / "lms").abspath(),
-]
+)
DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
MEDIA_ROOT = TEST_ROOT / "uploads"
diff --git a/lms/envs/common.py b/lms/envs/common.py
index 6ad37a6ca9..071661a06d 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -396,7 +396,7 @@ COURSES_ROOT = ENV_ROOT / "data"
DATA_DIR = COURSES_ROOT
# comprehensive theming system
-COMPREHENSIVE_THEME_DIR = REPO_ROOT / "themes"
+COMPREHENSIVE_THEME_DIR = ""
# TODO: Remove the rest of the sys.path modification here and in cms/envs/common.py
sys.path.append(REPO_ROOT)
@@ -485,7 +485,6 @@ TEMPLATES = [
'loaders': [
# We have to use mako-aware template loaders to be able to include
# mako templates inside django templates (such as main_django.html).
- 'openedx.core.djangoapps.theming.template_loaders.ThemeFilesystemLoader',
'edxmako.makoloader.MakoFilesystemLoader',
'edxmako.makoloader.MakoAppDirectoriesLoader',
],
@@ -785,6 +784,7 @@ SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
CMS_BASE = 'localhost:8001'
# Site info
+SITE_ID = 1
SITE_NAME = "example.com"
HTTPS = 'on'
ROOT_URLCONF = 'lms.urls'
@@ -1145,10 +1145,6 @@ MIDDLEWARE_CLASSES = (
# catches any uncaught RateLimitExceptions and returns a 403 instead of a 500
'ratelimitbackend.middleware.RateLimitMiddleware',
-
- # django current site middleware with default site
- 'django_sites_extensions.middleware.CurrentSiteWithDefaultMiddleware',
-
# needs to run after locale middleware (or anything that modifies the request context)
'edxmako.middleware.MakoMiddleware',
@@ -1182,7 +1178,7 @@ STATICFILES_STORAGE = 'openedx.core.storage.ProductionStorage'
# List of finder classes that know how to find static files in various locations.
# Note: the pipeline finder is included to be able to discover optimized files
STATICFILES_FINDERS = [
- 'openedx.core.djangoapps.theming.finders.ThemeFilesFinder',
+ 'openedx.core.djangoapps.theming.finders.ComprehensiveThemeFinder',
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
'pipeline.finders.PipelineFinder',
@@ -2878,10 +2874,6 @@ WIKI_REQUEST_CACHE_MIDDLEWARE_CLASS = "request_cache.middleware.RequestCache"
# Dafault site id to use in case there is no site that matches with the request headers.
DEFAULT_SITE_ID = 1
-# Cache time out settings
-# by Comprehensive Theme system
-THEME_CACHE_TIMEOUT = 30 * 60
-
# API access management
API_ACCESS_MANAGER_EMAIL = 'api-access@example.com'
API_ACCESS_FROM_EMAIL = 'api-requests@example.com'
diff --git a/lms/envs/devstack.py b/lms/envs/devstack.py
index 350ccafe33..1657155966 100644
--- a/lms/envs/devstack.py
+++ b/lms/envs/devstack.py
@@ -99,7 +99,7 @@ STATICFILES_STORAGE = 'openedx.core.storage.DevelopmentStorage'
# Revert to the default set of finders as we don't want the production pipeline
STATICFILES_FINDERS = [
- 'openedx.core.djangoapps.theming.finders.ThemeFilesFinder',
+ 'openedx.core.djangoapps.theming.finders.ComprehensiveThemeFinder',
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
]
diff --git a/lms/envs/devstack_optimized.py b/lms/envs/devstack_optimized.py
index e0210ef6db..251ed4f343 100644
--- a/lms/envs/devstack_optimized.py
+++ b/lms/envs/devstack_optimized.py
@@ -38,6 +38,6 @@ STATIC_URL = "/static/"
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
)
-STATICFILES_DIRS = [
+STATICFILES_DIRS = (
(TEST_ROOT / "staticfiles" / "lms").abspath(),
-]
+)
diff --git a/lms/envs/test.py b/lms/envs/test.py
index 69ce227544..ba43f30574 100644
--- a/lms/envs/test.py
+++ b/lms/envs/test.py
@@ -429,9 +429,6 @@ openid.oidutil.log = lambda message, level=0: None
PLATFORM_NAME = "edX"
SITE_NAME = "edx.org"
-# use default site for tests
-SITE_ID = 1
-
# set up some testing for microsites
FEATURES['USE_MICROSITES'] = True
MICROSITE_ROOT_DIR = COMMON_ROOT / 'test' / 'test_microsites'
@@ -501,8 +498,6 @@ MICROSITE_CONFIGURATION = {
MICROSITE_TEST_HOSTNAME = 'testmicrosite.testserver'
MICROSITE_LOGISTRATION_HOSTNAME = 'logistration.testserver'
-TEST_THEME = COMMON_ROOT / "test" / "test-theme"
-
# add extra template directory for test-only templates
MAKO_TEMPLATES['main'].extend([
COMMON_ROOT / 'test' / 'templates',
diff --git a/lms/startup.py b/lms/startup.py
index 2762e900ab..b6e63c3232 100644
--- a/lms/startup.py
+++ b/lms/startup.py
@@ -20,7 +20,7 @@ from monkey_patch import (
import xmodule.x_module
import lms_xblock.runtime
-from openedx.core.djangoapps.theming.core import enable_comprehensive_theming
+from openedx.core.djangoapps.theming.core import enable_comprehensive_theme
from microsite_configuration import microsite
log = logging.getLogger(__name__)
@@ -40,7 +40,7 @@ def run():
# Comprehensive theming needs to be set up before django startup,
# because modifying django template paths after startup has no effect.
if settings.COMPREHENSIVE_THEME_DIR:
- enable_comprehensive_theming(settings.COMPREHENSIVE_THEME_DIR)
+ enable_comprehensive_theme(settings.COMPREHENSIVE_THEME_DIR)
# We currently use 2 template rendering engines, mako and django_templates,
# and one of them (django templates), requires the directories be added
diff --git a/lms/static/certificates/sass/_build.scss b/lms/static/certificates/sass/_build.scss
index 11bd2e9947..c97b1b0c37 100644
--- a/lms/static/certificates/sass/_build.scss
+++ b/lms/static/certificates/sass/_build.scss
@@ -1,15 +1,19 @@
// ------------------------------
// Open edX Certificates: Shared Build Compile
-// About: Sass compile for Open edX Certificates elements that are shared between LTR and RTL UI.
-// Configuration and vendor specific imports happen before this shared set of imports are compiled
-// in the main-*.scss files.
+// About: Sass compile for Open edX Certificates elements that are shared between LTR and RTL UI. Configuration and vendor specific imports happen before this shared set of imports are compiled in the main-*.scss files.
-// Configuration
+// ------------------------------
+// #CONFIG + LIB
+// ------------------------------
+@import 'lib';
@import 'config';
+@import '../../../../node_modules/edx-pattern-library/pattern-library/sass/edx-pattern-library';
-// Extensions
+// ------------------------------
+// #EXTENSIONS
+// ------------------------------
@import 'utilities';
@import 'base';
@import 'components';
diff --git a/lms/static/certificates/sass/_config.scss b/lms/static/certificates/sass/_config.scss
index 911c68dd9a..d8e3a022d5 100644
--- a/lms/static/certificates/sass/_config.scss
+++ b/lms/static/certificates/sass/_config.scss
@@ -1,10 +1,14 @@
// ------------------------------
// Open edX Certificates: Config
+// About: variable and configuration overrides
+
+// #VARIABLES
// ------------------------------
// #VARIABLES
// ------------------------------
+$pattern-library-path: '../../edx-pattern-library' !default;
// certificate characteristics
$cert-base-color: palette(grayscale-cool, dark);
diff --git a/lms/static/certificates/sass/_lib.scss b/lms/static/certificates/sass/_lib.scss
new file mode 100644
index 0000000000..83b0a55882
--- /dev/null
+++ b/lms/static/certificates/sass/_lib.scss
@@ -0,0 +1,9 @@
+// ------------------------------
+// Open edX Certificates: Main Style Compile
+
+// About: third party libraries and dependencies import
+
+
+@import '../../../../node_modules/edx-pattern-library/node_modules/bourbon/app/assets/stylesheets/bourbon';
+@import '../../../../node_modules/edx-pattern-library/node_modules/susy/sass/susy';
+@import '../../../../node_modules/edx-pattern-library/node_modules/breakpoint-sass/stylesheets/breakpoint';
diff --git a/lms/static/certificates/sass/_ltr.scss b/lms/static/certificates/sass/_ltr.scss
new file mode 100644
index 0000000000..392f53d3dd
--- /dev/null
+++ b/lms/static/certificates/sass/_ltr.scss
@@ -0,0 +1,23 @@
+// ------------------------------
+// Open edX Certificates: Main Style Compile
+
+// About: Sass partial for defining settings and utilities for LTR-centric layouts.
+
+// #SETTINGS
+// #LIB
+
+
+// ----------------------------
+// #SETTINGS
+// ----------------------------
+$layout-direction: ltr;
+
+// currently needed since platform Sass won't obey https://github.com/edx/ux-pattern-library/blob/master/pattern-library/sass/patterns/_grid.scss#L23
+$grid-direction-default: ltr;
+$grid-direction-reversed: ltr;
+
+
+// ----------------------------
+// #LIB
+// ----------------------------
+@import '../../../../node_modules/edx-pattern-library/node_modules/bi-app-sass/bi-app/bi-app-ltr';
diff --git a/lms/static/certificates/sass/_rtl.scss b/lms/static/certificates/sass/_rtl.scss
new file mode 100644
index 0000000000..ba219f69f8
--- /dev/null
+++ b/lms/static/certificates/sass/_rtl.scss
@@ -0,0 +1,23 @@
+// ------------------------------
+// Open edX Certificates: Main Style Compile
+
+// About: Sass partial for defining settings and utilities for LTR-centric layouts.
+
+// #SETTINGS
+// #LIB
+
+
+// ----------------------------
+// #SETTINGS
+// ----------------------------
+$layout-direction: rtl;
+
+// currently needed since platform Sass won't obey https://github.com/edx/ux-pattern-library/blob/master/pattern-library/sass/patterns/_grid.scss#L23
+$grid-direction-default: rtl;
+$grid-direction-reversed: ltr;
+
+
+// ----------------------------
+// #LIB
+// ----------------------------
+@import '../../../../node_modules/edx-pattern-library/node_modules/bi-app-sass/bi-app/bi-app-rtl';
diff --git a/lms/static/certificates/sass/main-ltr.scss b/lms/static/certificates/sass/main-ltr.scss
index 41f638c2b4..db0e51c41e 100644
--- a/lms/static/certificates/sass/main-ltr.scss
+++ b/lms/static/certificates/sass/main-ltr.scss
@@ -3,13 +3,16 @@
// About: Sass compile for the Open edX Certificates Elements.
-// NOTE: This is the left to right (LTR) configured style compile.
-// It should mirror main-rtl w/ the exception of bi-app references.
+// NOTE: This is the left to right (LTR) configured style compile. It should mirror main-rtl w/ the exception of bi-app references.
-// Load the LTR version of the edX Pattern Library
-$pattern-library-path: '../../edx-pattern-library' !default;
-@import 'edx-pattern-library/pattern-library/sass/edx-pattern-library-ltr';
+// ------------------------------
+// #CONFIG - layout direction
+// ------------------------------
+@import 'ltr'; // LTR-specifc settings and utilities
-// Load the shared build
-@import 'build';
+
+// ------------------------------
+// #BUILD
+// ------------------------------
+@import 'build'; // shared compile/build order for both LTR and RTL UI
diff --git a/lms/static/certificates/sass/main-rtl.scss b/lms/static/certificates/sass/main-rtl.scss
index faea15b67e..54a6ec443c 100644
--- a/lms/static/certificates/sass/main-rtl.scss
+++ b/lms/static/certificates/sass/main-rtl.scss
@@ -3,13 +3,16 @@
// About: Sass compile for the Open edX Certificates Elements.
-// NOTE: This is the right to left (RTL) configured style compile.
-// It should mirror main-ltr w/ the exception of bi-app references.
+// NOTE: This is the right to left (RTL) configured style compile. It should mirror main-ltr w/ the exception of bi-app references.
-// Load the RTL version of the edX Pattern Library
-$pattern-library-path: '../../edx-pattern-library' !default;
-@import 'edx-pattern-library/pattern-library/sass/edx-pattern-library-rtl';
+// ------------------------------
+// #CONFIG - layout direction
+// ------------------------------
+@import 'rtl'; // RTL-specifc settings and utilities
-// Load the shared build
-@import 'build';
+
+// ------------------------------
+// #BUILD
+// ------------------------------
+@import 'build'; // shared compile/build order for both LTR and RTL UI
diff --git a/lms/static/sass/_build-lms-v2.scss b/lms/static/sass/_build-lms-v2.scss
deleted file mode 100644
index a982b28c2a..0000000000
--- a/lms/static/sass/_build-lms-v2.scss
+++ /dev/null
@@ -1,9 +0,0 @@
-// ------------------------------
-// LMS: Shared Build Compile
-// Version 2 - introduces the Pattern Library
-
-
-// Configuration
-@import 'config';
-
-// Extensions
diff --git a/lms/static/sass/_build-lms-v1.scss b/lms/static/sass/_build-lms.scss
similarity index 100%
rename from lms/static/sass/_build-lms-v1.scss
rename to lms/static/sass/_build-lms.scss
diff --git a/lms/static/sass/_config.scss b/lms/static/sass/_config.scss
deleted file mode 100644
index e3a6ab63c1..0000000000
--- a/lms/static/sass/_config.scss
+++ /dev/null
@@ -1,7 +0,0 @@
-// ------------------------------
-// LMS configuration settings
-
-
-// ------------------------------
-// #VARIABLES
-// ------------------------------
diff --git a/lms/static/sass/partials/base/_variables.scss b/lms/static/sass/base/_variables.scss
similarity index 100%
rename from lms/static/sass/partials/base/_variables.scss
rename to lms/static/sass/base/_variables.scss
diff --git a/lms/static/sass/lms-main-v1-rtl.scss b/lms/static/sass/lms-main-rtl.scss
similarity index 78%
rename from lms/static/sass/lms-main-v1-rtl.scss
rename to lms/static/sass/lms-main-rtl.scss
index 0988836b42..7ab3e3cd35 100644
--- a/lms/static/sass/lms-main-v1-rtl.scss
+++ b/lms/static/sass/lms-main-rtl.scss
@@ -1,5 +1,4 @@
-// LMS - CSS application architecture
-// Version 1 styling (pre-Pattern Library)
+// lms - css application architecture
// ====================
// libs and resets *do not edit*
@@ -19,4 +18,4 @@
// theme, for old-style deprecated theming.
//
-@import 'build-lms-v1'; // shared app style assets/rendering
+@import 'build-lms'; // shared app style assets/rendering
diff --git a/lms/static/sass/lms-main-v2-rtl.scss b/lms/static/sass/lms-main-v2-rtl.scss
deleted file mode 100644
index 195b752386..0000000000
--- a/lms/static/sass/lms-main-v2-rtl.scss
+++ /dev/null
@@ -1,14 +0,0 @@
-// ------------------------------
-// LMS main styling
-// Version 2 - introduces the Pattern Library
-
-// NOTE: This is the right-to-left (RTL) configured style compile.
-// It should mirror lms-main-v2 w/ the exception of bi-app references.
-
-
-// Load the RTL version of the edX Pattern Library
-$pattern-library-path: '../edx-pattern-library' !default;
-@import 'edx-pattern-library/pattern-library/sass/edx-pattern-library-rtl';
-
-// Load the shared build
-@import 'build-lms-v2';
diff --git a/lms/static/sass/lms-main-v2.scss b/lms/static/sass/lms-main-v2.scss
deleted file mode 100644
index ca43dfc041..0000000000
--- a/lms/static/sass/lms-main-v2.scss
+++ /dev/null
@@ -1,14 +0,0 @@
-// ------------------------------
-// LMS main styling
-// Version 2 - introduces the Pattern Library
-
-// NOTE: This is the left-to-right (LTR) configured style compile.
-// It should mirror lms-main-v2-rtl w/ the exception of bi-app references.
-
-
-// Load the RTL version of the edX Pattern Library
-$pattern-library-path: '../edx-pattern-library' !default;
-@import 'edx-pattern-library/pattern-library/sass/edx-pattern-library-ltr';
-
-// Load the shared build
-@import 'build-lms-v2';
diff --git a/lms/static/sass/lms-main-v1.scss b/lms/static/sass/lms-main.scss
similarity index 76%
rename from lms/static/sass/lms-main-v1.scss
rename to lms/static/sass/lms-main.scss
index ca36fb3acb..1a79ca0442 100644
--- a/lms/static/sass/lms-main-v1.scss
+++ b/lms/static/sass/lms-main.scss
@@ -1,5 +1,4 @@
-// LMS - CSS application architecture
-// Version 1 styling (pre-Pattern Library)
+// lms - css application architecture
// ====================
// libs and resets *do not edit*
@@ -18,4 +17,4 @@
// theme, for old-style deprecated theming.
//
-@import 'build-lms-v1'; // shared app style assets/rendering
+@import 'build-lms'; // shared app style assets/rendering
diff --git a/lms/templates/dashboard.html b/lms/templates/dashboard.html
index 0550c5c0d5..fa54c3e540 100644
--- a/lms/templates/dashboard.html
+++ b/lms/templates/dashboard.html
@@ -8,6 +8,7 @@ from django.template import RequestContext
import third_party_auth
from third_party_auth import pipeline
from openedx.core.djangolib.js_utils import dump_js_escaped_json, js_escaped_string
+from openedx.core.djangolib.markup import Text, HTML
%>
<%
@@ -200,7 +201,7 @@ from openedx.core.djangolib.js_utils import dump_js_escaped_json, js_escaped_str
- ${_("Email Settings for {course_number}").format(course_number='')}
+ ${Text(_("Email Settings for {course_number}")).format(course_number=HTML(''))}
,
## Translators: this text gives status on if the modal interface (a menu or piece of UI that takes the full focus of the screen) is open or not
${_("window open")}
diff --git a/lms/templates/dashboard/_dashboard_course_listing.html b/lms/templates/dashboard/_dashboard_course_listing.html
index de9e3184af..92ae61d505 100644
--- a/lms/templates/dashboard/_dashboard_course_listing.html
+++ b/lms/templates/dashboard/_dashboard_course_listing.html
@@ -8,6 +8,7 @@ from django.utils.translation import ungettext
from django.core.urlresolvers import reverse
from course_modes.models import CourseMode
from course_modes.helpers import enrollment_mode_display
+from openedx.core.djangolib.js_utils import dump_js_escaped_json
from openedx.core.djangolib.markup import Text, HTML
from student.helpers import (
VERIFY_STATUS_NEED_TO_VERIFY,
@@ -317,7 +318,13 @@ from student.helpers import (
${_('Your verification will expire soon!')}
## Translators: start_link and end_link will be replaced with HTML tags;
## please do not translate these.
-
${Text(_('Your current verification will expire before the verification deadline for this course. {start_link}Re-verify your identity now{end_link} using a webcam and a government-issued ID.')).format(start_link=HTML(''.format(href=reverse('verify_student_reverify'))), end_link=HTML(''))}
+
${Text(_('Your current verification will expire before the verification deadline '
+ 'for this course. {start_link}Re-verify your identity now{end_link} using a webcam and a '
+ 'government-issued ID.')).format(
+ start_link=HTML('').format(href=reverse('verify_student_reverify')),
+ end_link=HTML('')
+ )}
+
${Text(_("You must successfully complete {link_start}{prc_display}{link_end} before you begin this course.")).format(
- link_start=HTML(''.format(prc_target)),
+ link_start=HTML('').format(prc_target),
link_end=HTML(''),
prc_display=course_requirements['courses'][0]['display'],
)}
@@ -409,7 +416,7 @@ from student.helpers import (