diff --git a/.gitignore b/.gitignore index e8cd280ccd..ecb4236cd4 100644 --- a/.gitignore +++ b/.gitignore @@ -76,13 +76,15 @@ lms/static/certificates/css/ cms/static/css/ ### Styling generated from templates -lms/static/sass/lms-main.scss -lms/static/sass/lms-main-rtl.scss -lms/static/sass/lms-course.scss -lms/static/sass/lms-course-rtl.scss -lms/static/sass/lms-footer.scss -lms/static/sass/lms-footer-rtl.scss - +lms/static/sass/*.css +lms/static/sass/*.css.map +lms/static/certificates/sass/*.css +lms/static/themed_sass/ +cms/static/css/ +cms/static/sass/*.css +cms/static/sass/*.css.map +cms/static/themed_sass/ +themes/**/css/*.css ### Logging artifacts log/ diff --git a/cms/envs/common.py b/cms/envs/common.py index 56899b8807..5f2cddc4a3 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -36,7 +36,7 @@ import lms.envs.common # Although this module itself may not use these imported variables, other dependent modules may. from lms.envs.common import ( USE_TZ, TECH_SUPPORT_EMAIL, PLATFORM_NAME, BUGS_EMAIL, DOC_STORE_CONFIG, DATA_DIR, ALL_LANGUAGES, WIKI_ENABLED, - update_module_store_settings, ASSET_IGNORE_REGEX, COPYRIGHT_YEAR, PARENTAL_CONSENT_AGE_LIMIT, + update_module_store_settings, ASSET_IGNORE_REGEX, COPYRIGHT_YEAR, PARENTAL_CONSENT_AGE_LIMIT, COMP_THEME_DIR, # The following PROFILE_IMAGE_* settings are included as they are # indirectly accessed through the email opt-in API, which is # technically accessible through the CMS via legacy URLs. @@ -741,6 +741,9 @@ INSTALLED_APPS = ( 'static_replace', 'require', + # Theming + 'openedx.core.djangoapps.theming', + # comment common 'django_comment_common', diff --git a/cms/static/sass/_variables.scss b/cms/static/sass/_variables.scss index acdc7e31b3..ad20b57221 100644 --- a/cms/static/sass/_variables.scss +++ b/cms/static/sass/_variables.scss @@ -2,6 +2,7 @@ // ==================== // Table of Contents +// * +Paths // * +Grid // * +Fonts // * +Colors - Utility @@ -15,6 +16,10 @@ $baseline: 20px; +// +Paths +// ==================== +$static-path: '..' !default; + // +Grid // ==================== $gw-column: ($baseline*3); diff --git a/cms/static/sass/assets/_fonts.scss b/cms/static/sass/assets/_fonts.scss index 65c1c91f3e..4234969679 100644 --- a/cms/static/sass/assets/_fonts.scss +++ b/cms/static/sass/assets/_fonts.scss @@ -3,9 +3,9 @@ @font-face { font-family: 'Open Sans'; src: - url('../fonts/OpenSans/OpenSans-Light-webfont.woff2') format('woff2'), - url('../fonts/OpenSans/OpenSans-Light-webfont.woff') format('woff'), - url('../fonts/OpenSans/OpenSans-Light-webfont.ttf') format('truetype'); + url('#{$static-path}/fonts/OpenSans/OpenSans-Light-webfont.woff2') format('woff2'), + url('#{$static-path}/fonts/OpenSans/OpenSans-Light-webfont.woff') format('woff'), + url('#{$static-path}/fonts/OpenSans/OpenSans-Light-webfont.ttf') format('truetype'); font-weight: 300; font-style: normal; } @@ -13,9 +13,9 @@ @font-face { font-family: 'Open Sans'; src: - url('../fonts/OpenSans/OpenSans-LightItalic-webfont.woff2') format('woff2'), - url('../fonts/OpenSans/OpenSans-LightItalic-webfont.woff') format('woff'), - url('../fonts/OpenSans/OpenSans-LightItalic-webfont.ttf') format('truetype'); + url('#{$static-path}/fonts/OpenSans/OpenSans-LightItalic-webfont.woff2') format('woff2'), + url('#{$static-path}/fonts/OpenSans/OpenSans-LightItalic-webfont.woff') format('woff'), + url('#{$static-path}/fonts/OpenSans/OpenSans-LightItalic-webfont.ttf') format('truetype'); font-weight: 300; font-style: italic; } @@ -23,9 +23,9 @@ @font-face { font-family: 'Open Sans'; src: - url('../fonts/OpenSans/OpenSans-Regular-webfont.woff2') format('woff2'), - url('../fonts/OpenSans/OpenSans-Regular-webfont.woff') format('woff'), - url('../fonts/OpenSans/OpenSans-Regular-webfont.ttf') format('truetype'); + url('#{$static-path}/fonts/OpenSans/OpenSans-Regular-webfont.woff2') format('woff2'), + url('#{$static-path}/fonts/OpenSans/OpenSans-Regular-webfont.woff') format('woff'), + url('#{$static-path}/fonts/OpenSans/OpenSans-Regular-webfont.ttf') format('truetype'); font-weight: 400; font-style: normal; } @@ -33,9 +33,9 @@ @font-face { font-family: 'Open Sans'; src: - url('../fonts/OpenSans/OpenSans-Italic-webfont.woff2') format('woff2'), - url('../fonts/OpenSans/OpenSans-Italic-webfont.woff') format('woff'), - url('../fonts/OpenSans/OpenSans-Italic-webfont.ttf') format('truetype'); + url('#{$static-path}/fonts/OpenSans/OpenSans-Italic-webfont.woff2') format('woff2'), + url('#{$static-path}/fonts/OpenSans/OpenSans-Italic-webfont.woff') format('woff'), + url('#{$static-path}/fonts/OpenSans/OpenSans-Italic-webfont.ttf') format('truetype'); font-weight: 400; font-style: italic; } @@ -43,9 +43,9 @@ @font-face { font-family: 'Open Sans'; src: - url('../fonts/OpenSans/OpenSans-Semibold-webfont.woff2') format('woff2'), - url('../fonts/OpenSans/OpenSans-Semibold-webfont.woff') format('woff'), - url('../fonts/OpenSans/OpenSans-Semibold-webfont.ttf') format('truetype'); + url('#{$static-path}/fonts/OpenSans/OpenSans-Semibold-webfont.woff2') format('woff2'), + url('#{$static-path}/fonts/OpenSans/OpenSans-Semibold-webfont.woff') format('woff'), + url('#{$static-path}/fonts/OpenSans/OpenSans-Semibold-webfont.ttf') format('truetype'); font-weight: 600; font-style: normal; } @@ -53,9 +53,9 @@ @font-face { font-family: 'Open Sans'; src: - url('../fonts/OpenSans/OpenSans-SemiboldItalic-webfont.woff2') format('woff2'), - url('../fonts/OpenSans/OpenSans-SemiboldItalic-webfont.woff') format('woff'), - url('../fonts/OpenSans/OpenSans-SemiboldItalic-webfont.ttf') format('truetype'); + url('#{$static-path}/fonts/OpenSans/OpenSans-SemiboldItalic-webfont.woff2') format('woff2'), + url('#{$static-path}/fonts/OpenSans/OpenSans-SemiboldItalic-webfont.woff') format('woff'), + url('#{$static-path}/fonts/OpenSans/OpenSans-SemiboldItalic-webfont.ttf') format('truetype'); font-weight: 600; font-style: italic; } @@ -63,9 +63,9 @@ @font-face { font-family: 'Open Sans'; src: - url('../fonts/OpenSans/OpenSans-Bold-webfont.woff2') format('woff2'), - url('../fonts/OpenSans/OpenSans-Bold-webfont.woff') format('woff'), - url('../fonts/OpenSans/OpenSans-Bold-webfont.ttf') format('truetype'); + url('#{$static-path}/fonts/OpenSans/OpenSans-Bold-webfont.woff2') format('woff2'), + url('#{$static-path}/fonts/OpenSans/OpenSans-Bold-webfont.woff') format('woff'), + url('#{$static-path}/fonts/OpenSans/OpenSans-Bold-webfont.ttf') format('truetype'); font-weight: 700; font-style: normal; } @@ -73,9 +73,9 @@ @font-face { font-family: 'Open Sans'; src: - url('../fonts/OpenSans/OpenSans-BoldItalic-webfont.woff2') format('woff2'), - url('../fonts/OpenSans/OpenSans-BoldItalic-webfont.woff') format('woff'), - url('../fonts/OpenSans/OpenSans-BoldItalic-webfont.ttf') format('truetype'); + url('#{$static-path}/fonts/OpenSans/OpenSans-BoldItalic-webfont.woff2') format('woff2'), + url('#{$static-path}/fonts/OpenSans/OpenSans-BoldItalic-webfont.woff') format('woff'), + url('#{$static-path}/fonts/OpenSans/OpenSans-BoldItalic-webfont.ttf') format('truetype'); font-weight: 700; font-style: italic; } diff --git a/cms/static/sass/assets/_graphics.scss b/cms/static/sass/assets/_graphics.scss index 9b0253b085..e8a3f8c8a5 100644 --- a/cms/static/sass/assets/_graphics.scss +++ b/cms/static/sass/assets/_graphics.scss @@ -7,7 +7,7 @@ width: 7px; height: 22px; margin-left: ($baseline/2); - background: url(../images/drag-handles.png) no-repeat; + background: url('#{$static-path}/images/drag-handles.png') no-repeat; cursor: move; } @@ -15,33 +15,33 @@ display: inline-block; width: ($baseline*3); height: ($baseline*3); - background: url(../images/large-advanced-icon.png) center no-repeat; + background: url('#{$static-path}/images/large-advanced-icon.png') center no-repeat; } .large-discussion-icon { display: inline-block; width: ($baseline*3); height: ($baseline*3); - background: url(../images/large-discussion-icon.png) center no-repeat; + background: url('#{$static-path}/images/large-discussion-icon.png') center no-repeat; } .large-html-icon { display: inline-block; width: ($baseline*3); height: ($baseline*3); - background: url(../images/large-html-icon.png) center no-repeat; + background: url('#{$static-path}/images/large-html-icon.png') center no-repeat; } .large-problem-icon { display: inline-block; width: ($baseline*3); height: ($baseline*3); - background: url(../images/large-problem-icon.png) center no-repeat; + background: url('#{$static-path}/images/large-problem-icon.png') center no-repeat; } .large-video-icon { display: inline-block; width: ($baseline*3); height: ($baseline*3); - background: url(../images/large-video-icon.png) center no-repeat; + background: url('#{$static-path}/images/large-video-icon.png') center no-repeat; } diff --git a/cms/static/sass/elements/_controls.scss b/cms/static/sass/elements/_controls.scss index 45df38f5ec..57ce75625e 100644 --- a/cms/static/sass/elements/_controls.scss +++ b/cms/static/sass/elements/_controls.scss @@ -295,12 +295,12 @@ // CASE: right to left layout @include rtl { - background: transparent url("../images/drag-handles.png") no-repeat left center; + background: transparent url("#{$static-path}/images/drag-handles.png") no-repeat left center; } // CASE: left to right layout @include ltr { - background: transparent url("../images/drag-handles.png") no-repeat right center; + background: transparent url("#{$static-path}/images/drag-handles.png") no-repeat right center; } } diff --git a/cms/static/sass/elements/_creative-commons.scss b/cms/static/sass/elements/_creative-commons.scss index d14351f83b..dc3a5a0b05 100644 --- a/cms/static/sass/elements/_creative-commons.scss +++ b/cms/static/sass/elements/_creative-commons.scss @@ -1,10 +1,10 @@ @font-face { font-family: 'CreativeCommons'; - src: url('../fonts/CreativeCommons/cc.eot'); - src: url('../fonts/CreativeCommons/cc.eot#iefix') format('embedded-opentype'), - url('../fonts/CreativeCommons/cc.woff') format('woff'), - url('../fonts/CreativeCommons/cc.ttf') format('truetype'), - url('../fonts/CreativeCommons/cc.svg#CreativeCommons') format('svg'); + src: url('#{$static-path}/fonts/CreativeCommons/cc.eot'); + src: url('#{$static-path}/fonts/CreativeCommons/cc.eot#iefix') format('embedded-opentype'), + url('#{$static-path}/fonts/CreativeCommons/cc.woff') format('woff'), + url('#{$static-path}/fonts/CreativeCommons/cc.ttf') format('truetype'), + url('#{$static-path}/fonts/CreativeCommons/cc.svg#CreativeCommons') format('svg'); font-weight: normal; font-style: normal; } diff --git a/cms/static/sass/elements/_forms.scss b/cms/static/sass/elements/_forms.scss index 307bd31d98..7bec93b1ad 100644 --- a/cms/static/sass/elements/_forms.scss +++ b/cms/static/sass/elements/_forms.scss @@ -456,7 +456,7 @@ input.search { @include box-sizing(border-box); border: 1px solid $darkGrey; border-radius: 20px; - background: url(../images/search-icon.png) no-repeat 8px 7px #edf1f5; + background: url('#{$static-path}/images/search-icon.png') no-repeat 8px 7px #edf1f5; font-family: 'Open Sans', sans-serif; color: $baseFontColor; outline: 0; diff --git a/cms/static/sass/elements/_uploaded-assets.scss b/cms/static/sass/elements/_uploaded-assets.scss index 2253d2e660..e8f98443bd 100644 --- a/cms/static/sass/elements/_uploaded-assets.scss +++ b/cms/static/sass/elements/_uploaded-assets.scss @@ -100,7 +100,7 @@ } &.is-locked { - background-image: url('../images/bg-micro-stripes.png'); + background-image: url('#{$static-path}/images/bg-micro-stripes.png'); background-position: 0 0; background-repeat: repeat; } diff --git a/cms/static/sass/views/_assets.scss b/cms/static/sass/views/_assets.scss index 24284d9dfd..f0df189d42 100644 --- a/cms/static/sass/views/_assets.scss +++ b/cms/static/sass/views/_assets.scss @@ -299,7 +299,7 @@ } &.is-locked { - background-image: url('../images/bg-micro-stripes.png'); + background-image: url('#{$static-path}/images/bg-micro-stripes.png'); background-position: 0 0; background-repeat: repeat; } diff --git a/cms/static/sass/views/_static-pages.scss b/cms/static/sass/views/_static-pages.scss index a9cf9c39d9..302b561551 100644 --- a/cms/static/sass/views/_static-pages.scss +++ b/cms/static/sass/views/_static-pages.scss @@ -197,7 +197,7 @@ } .drag-handle { - background: url(../images/drag-handles.png) center no-repeat #fff; + background: url('#{$static-path}/images/drag-handles.png') center no-repeat #fff; } } @@ -210,10 +210,10 @@ width: 35px; height: 100%; border: none; - background: url(../images/drag-handles.png) center no-repeat #fff; + background: url('#{$static-path}/images/drag-handles.png') center no-repeat #fff; &:hover { - background: url(../images/drag-handles.png) center no-repeat #fff; + background: url('#{$static-path}/images/drag-handles.png') center no-repeat #fff; } &.is-fixed { diff --git a/cms/templates/widgets/header.html b/cms/templates/widgets/header.html index e4e2a9cf22..836a618c8f 100644 --- a/cms/templates/widgets/header.html +++ b/cms/templates/widgets/header.html @@ -12,11 +12,7 @@

- % if settings.FEATURES.get('IS_EDX_DOMAIN', False): - ${settings.STUDIO_NAME} - % else: - ${settings.STUDIO_NAME} - % endif + ${settings.STUDIO_NAME}

% if context_course: diff --git a/common/djangoapps/edxmako/__init__.py b/common/djangoapps/edxmako/__init__.py index 981d640bc0..e7b68f44c3 100644 --- a/common/djangoapps/edxmako/__init__.py +++ b/common/djangoapps/edxmako/__init__.py @@ -13,4 +13,4 @@ # limitations under the License. LOOKUP = {} -from .paths import add_lookup, lookup_template, clear_lookups +from .paths import add_lookup, lookup_template, clear_lookups, save_lookups diff --git a/common/djangoapps/edxmako/management/commands/preprocess_assets.py b/common/djangoapps/edxmako/management/commands/preprocess_assets.py index 43efd4537d..805981327e 100644 --- a/common/djangoapps/edxmako/management/commands/preprocess_assets.py +++ b/common/djangoapps/edxmako/management/commands/preprocess_assets.py @@ -8,67 +8,53 @@ For this to work, assets need to be named with the appropriate template extension (e.g., .mako for Mako templates). Currently Mako is the only template engine supported. """ + import os - -from django.core.management.base import NoArgsCommand -from django.conf import settings - -from mako.template import Template import textwrap +from django.core.management.base import BaseCommand +from django.conf import settings -class Command(NoArgsCommand): + +class Command(BaseCommand): """ Basic management command to preprocess asset template files. """ help = "Preprocess asset template files to ready them for compilation." - def handle_noargs(self, **options): - """ - Walk over all of the static files directories specified in the - settings file, looking for asset template files (indicated by - a file extension like .mako). - """ - for staticfiles_dir in getattr(settings, "STATICFILES_DIRS", []): - # Cribbed from the django-staticfiles app at: - # https://github.com/jezdez/django-staticfiles/blob/develop/staticfiles/finders.py#L52 - if isinstance(staticfiles_dir, (list, tuple)): - prefix, staticfiles_dir = staticfiles_dir + def handle(self, *args, **options): + theme_name = getattr(settings, "THEME_NAME", None) + use_custom_theme = settings.FEATURES.get("USE_CUSTOM_THEME", False) + if not use_custom_theme or not theme_name: + # No custom theme, nothing to do! + return - # Walk over the current static files directory tree, - # preprocessing files that have a template extension. - for root, dirs, files in os.walk(staticfiles_dir): - for filename in files: - outfile, extension = os.path.splitext(filename) - # We currently only handle Mako templates - if extension == ".mako": - self.__preprocess(os.path.join(root, filename), - os.path.join(root, outfile)) + dest_dir = args[-1] + for source_file in args[:-1]: + self.process_one_file(source_file, dest_dir, theme_name) - def __context(self): - """ - Return a dict that contains all of the available context - variables to the asset template. - """ - # TODO: do we need to include anything else? - # TODO: do this with the django-settings-context-processor - return { - "FEATURES": settings.FEATURES, - "THEME_NAME": getattr(settings, "THEME_NAME", None), - } + def process_one_file(self, source_file, dest_dir, theme_name): + """Pre-process a .scss file to replace our markers with real code.""" + with open(source_file) as fsource: + original_content = fsource.read() - def __preprocess(self, infile, outfile): - """ - Run `infile` through the Mako template engine, storing the - result in `outfile`. - """ - with open(outfile, "w") as _outfile: - _outfile.write(textwrap.dedent("""\ - /* - * This file is dynamically generated and ignored by Git. - * DO NOT MAKE CHANGES HERE. Instead, go edit its template: - * %s - */ - """ % infile)) - _outfile.write(Template(filename=str(infile)).render(env=self.__context())) + content = original_content.replace( + "//", + "@import '{}';".format(theme_name), + ) + + if content != original_content: + if not os.path.exists(dest_dir): + os.makedirs(dest_dir) + dest_file = os.path.join(dest_dir, os.path.basename(source_file)) + with open(dest_file, "w") as fout: + fout.write(textwrap.dedent("""\ + /* + * This file is dynamically generated and ignored by Git. + * DO NOT MAKE CHANGES HERE. Instead, go edit its source: + * {} + */ + \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n + """.format(source_file))) + fout.write(content) diff --git a/common/djangoapps/edxmako/paths.py b/common/djangoapps/edxmako/paths.py index 12cbcdf1d3..18ddb9eafd 100644 --- a/common/djangoapps/edxmako/paths.py +++ b/common/djangoapps/edxmako/paths.py @@ -3,6 +3,7 @@ Set up lookup paths for mako templates. """ import hashlib +import contextlib import os import pkg_resources @@ -21,6 +22,9 @@ class DynamicTemplateLookup(TemplateLookup): super(DynamicTemplateLookup, self).__init__(*args, **kwargs) self.__original_module_directory = self.template_args['module_directory'] + def __repr__(self): + return "<{0.__class__.__name__} {0.directories}>".format(self) + def add_directory(self, directory, prepend=False): """ Add a new directory to the template lookup path. @@ -78,3 +82,26 @@ def lookup_template(namespace, name): Look up a Mako template by namespace and name. """ return LOOKUP[namespace].get_template(name) + + +@contextlib.contextmanager +def save_lookups(): + """ + A context manager to save and restore the Mako template lookup path. + + Useful for testing. + + """ + # Make a copy of the list of directories for each namespace. + namespace_dirs = {namespace: list(look.directories) for namespace, look in LOOKUP.items()} + + try: + yield + finally: + # Get rid of all the lookups. + LOOKUP.clear() + + # Re-create the lookups from our saved list. + for namespace, directories in namespace_dirs.items(): + for directory in directories: + add_lookup(namespace, directory) diff --git a/common/djangoapps/pipeline_mako/templates/static_content.html b/common/djangoapps/pipeline_mako/templates/static_content.html index 213b132168..f62abf6635 100644 --- a/common/djangoapps/pipeline_mako/templates/static_content.html +++ b/common/djangoapps/pipeline_mako/templates/static_content.html @@ -2,6 +2,9 @@ from staticfiles.storage import staticfiles_storage from pipeline_mako import compressed_css, compressed_js from django.utils.translation import get_language_bidi +from mako.exceptions import TemplateLookupException + +from microsite_configuration import microsite %> <%def name='url(file, raw=False)'><% @@ -73,3 +76,15 @@ source, template_path = _loader.load_template_source(path) }).call(this, require || RequireJS.require); + +<%def name="optional_include_mako(file, with_microsite=False)"><% +# http://stackoverflow.com/q/21219531 +if with_microsite: + file = microsite.get_template_path(file) +try: + tmpl = self.get_template(file) +except TemplateLookupException: + pass +else: + tmpl.render_context(context) +%> diff --git a/common/djangoapps/pipeline_mako/templates/test_exists.html b/common/djangoapps/pipeline_mako/templates/test_exists.html new file mode 100644 index 0000000000..1cc4d19d6c --- /dev/null +++ b/common/djangoapps/pipeline_mako/templates/test_exists.html @@ -0,0 +1,2 @@ +# Only for testing. +

This is test_exists.html

diff --git a/common/djangoapps/pipeline_mako/templates/test_optional_include_mako.html b/common/djangoapps/pipeline_mako/templates/test_optional_include_mako.html new file mode 100644 index 0000000000..b1bb06f806 --- /dev/null +++ b/common/djangoapps/pipeline_mako/templates/test_optional_include_mako.html @@ -0,0 +1,8 @@ +# A test template for optional_include_mako + +<%namespace name='static' file='static_content.html'/> + +

Welcome to test_optional_include_mako.html

+ +<%static:optional_include_mako file="test_exists.html" /> +<%static:optional_include_mako file="definitely_doesnt_exist.html" /> diff --git a/common/djangoapps/pipeline_mako/tests/__init__.py b/common/djangoapps/pipeline_mako/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/common/djangoapps/pipeline_mako/tests/test_static_content.py b/common/djangoapps/pipeline_mako/tests/test_static_content.py new file mode 100644 index 0000000000..f77a5fc216 --- /dev/null +++ b/common/djangoapps/pipeline_mako/tests/test_static_content.py @@ -0,0 +1,16 @@ +""" +Tests of pipeline_mako/templates/static_content.html +""" + +import unittest + +from edxmako.shortcuts import render_to_string + + +class TestStaticContent(unittest.TestCase): + """Tests for static_content.html""" + + def test_optional_include_mako(self): + out = render_to_string("test_optional_include_mako.html", {}) + self.assertIn("Welcome to test_optional_include_mako.html", out) + self.assertIn("This is test_exists.html", out) diff --git a/common/lib/xmodule/xmodule/css/capa/display.scss b/common/lib/xmodule/xmodule/css/capa/display.scss index 14ded8073d..8bd8b551db 100644 --- a/common/lib/xmodule/xmodule/css/capa/display.scss +++ b/common/lib/xmodule/xmodule/css/capa/display.scss @@ -111,7 +111,7 @@ iframe[seamless]{ } .inline-error { - color: darken($error-red, 11%); + color: darken($error-color, 11%); } div.problem-progress { @@ -321,7 +321,7 @@ div.problem { display: inline-block; width: 14px; height: 14px; - background: url('../images/unanswered-icon.png') center center no-repeat; + background: url('#{$static-path}/images/unanswered-icon.png') center center no-repeat; } } @@ -330,7 +330,7 @@ div.problem { display: inline-block; width: 25px; height: 20px; - background: url('../images/correct-icon.png') center center no-repeat; + background: url('#{$static-path}/images/correct-icon.png') center center no-repeat; } input { @@ -343,7 +343,7 @@ div.problem { display: inline-block; width: 20px; height: 20px; - background: url('../images/spinner.gif') center center no-repeat; + background: url('#{$static-path}/images/spinner.gif') center center no-repeat; } input { @@ -356,7 +356,7 @@ div.problem { display: inline-block; width: 20px; height: 20px; - background: url('../images/incorrect-icon.png') center center no-repeat; + background: url('#{$static-path}/images/incorrect-icon.png') center center no-repeat; } input { @@ -370,7 +370,7 @@ div.problem { display: inline-block; width: 20px; height: 20px; - background: url('../images/incorrect-icon.png') center center no-repeat; + background: url('#{$static-path}/images/incorrect-icon.png') center center no-repeat; } input { @@ -448,7 +448,7 @@ div.problem { top: 4px; width: 14px; height: 14px; - background: url('../images/unanswered-icon.png') center center no-repeat; + background: url('#{$static-path}/images/unanswered-icon.png') center center no-repeat; } &.processing, &.ui-icon-processing { @@ -457,7 +457,7 @@ div.problem { top: 6px; width: 25px; height: 20px; - background: url('../images/spinner.gif') center center no-repeat; + background: url('#{$static-path}/images/spinner.gif') center center no-repeat; } &.ui-icon-check { @@ -466,7 +466,7 @@ div.problem { top: 3px; width: 25px; height: 20px; - background: url('../images/correct-icon.png') center center no-repeat; + background: url('#{$static-path}/images/correct-icon.png') center center no-repeat; } &.incomplete, &.ui-icon-close { @@ -475,7 +475,7 @@ div.problem { top: 3px; width: 20px; height: 20px; - background: url('../images/incorrect-icon.png') center center no-repeat; + background: url('#{$static-path}/images/incorrect-icon.png') center center no-repeat; } } @@ -503,7 +503,7 @@ div.problem { .grading { margin: 0px 7px 0 0; padding-left: 25px; - background: url('../images/info-icon.png') left center no-repeat; + background: url('#{$static-path}/images/info-icon.png') left center no-repeat; text-indent: 0px; } @@ -967,8 +967,8 @@ div.problem { div.capa_reset { padding: 25px; - border: 1px solid $error-red; - background-color: lighten($error-red, 25%); + border: 1px solid $error-color; + background-color: lighten($error-color, 25%); border-radius: 3px; font-size: 1em; margin-top: $baseline/2; @@ -1097,7 +1097,7 @@ div.problem { .result-errors { margin: ($baseline/4); padding: ($baseline/2) ($baseline/2) ($baseline/2) ($baseline*2); - background: url('../images/incorrect-icon.png') center left no-repeat; + background: url('#{$static-path}/images/incorrect-icon.png') center left no-repeat; li { color: #b00; @@ -1129,7 +1129,7 @@ div.problem { } .result-correct { - background: url('../images/correct-icon.png') left 20px no-repeat; + background: url('#{$static-path}/images/correct-icon.png') left 20px no-repeat; .result-actual-output { color: #090; @@ -1137,7 +1137,7 @@ div.problem { } .result-incorrect { - background: url('../images/incorrect-icon.png') left 20px no-repeat; + background: url('#{$static-path}/images/incorrect-icon.png') left 20px no-repeat; .result-actual-output { color: #B00; @@ -1348,7 +1348,7 @@ div.problem { label.choicetextgroup_show_correct, section.choicetextgroup_show_correct { &:after { margin-left:15px; - content: url('../images/correct-icon.png'); + content: url('#{$static-path}/images/correct-icon.png'); } } @@ -1373,11 +1373,11 @@ div.problem .imageinput.capa_inputtype { } .correct { - background: url('../images/correct-icon.png') center center no-repeat; + background: url('#{$static-path}/images/correct-icon.png') center center no-repeat; } .incorrect { - background: url('../images/incorrect-icon.png') center center no-repeat; + background: url('#{$static-path}/images/incorrect-icon.png') center center no-repeat; } .partially-correct { diff --git a/common/lib/xmodule/xmodule/css/combinedopenended/display.scss b/common/lib/xmodule/xmodule/css/combinedopenended/display.scss index 8da727fecc..71908f9e5e 100644 --- a/common/lib/xmodule/xmodule/css/combinedopenended/display.scss +++ b/common/lib/xmodule/xmodule/css/combinedopenended/display.scss @@ -35,7 +35,7 @@ div.name{ } .inline-error { - color: darken($error-red, 10%); + color: darken($error-color, 10%); } section.combined-open-ended { @@ -178,7 +178,7 @@ section.combined-open-ended-status { float: right; width: 14px; height: 14px; - background: url('../images/unanswered-icon.png') center center no-repeat; + background: url('#{$static-path}/images/unanswered-icon.png') center center no-repeat; } &.correct { @@ -187,7 +187,7 @@ section.combined-open-ended-status { float: right; width: 25px; height: 20px; - background: url('../images/correct-icon.png') center center no-repeat; + background: url('#{$static-path}/images/correct-icon.png') center center no-repeat; } &.incorrect { @@ -196,7 +196,7 @@ section.combined-open-ended-status { float: right; width: 20px; height: 20px; - background: url('../images/incorrect-icon.png') center center no-repeat; + background: url('#{$static-path}/images/incorrect-icon.png') center center no-repeat; } } @@ -302,21 +302,21 @@ div.combined-rubric-container { label.choicegroup_correct { &:before { margin-right: ($baseline*0.75); - content: url('../images/correct-icon.png'); + content: url('#{$static-path}/images/correct-icon.png'); } } label.choicegroup_partialcorrect { &:before { margin-right: ($baseline*0.75); - content: url('../images/partially-correct-icon.png'); + content: url('#{$static-path}/images/partially-correct-icon.png'); } } label.choicegroup_incorrect { &:before { margin-right: ($baseline*0.75); - content: url('../images/incorrect-icon.png'); + content: url('#{$static-path}/images/incorrect-icon.png'); } } @@ -402,7 +402,7 @@ div.result-container { .result-errors { margin: ($baseline/4); padding: ($baseline/2) ($baseline/2) ($baseline/2) ($baseline*2); - background: url('../images/incorrect-icon.png') center left no-repeat; + background: url('#{$static-path}/images/incorrect-icon.png') center left no-repeat; li { color: #B00; @@ -564,7 +564,7 @@ section.open-ended-child { display: inline-block; width: 14px; height: 14px; - background: url('../images/unanswered-icon.png') center center no-repeat; + background: url('#{$static-path}/images/unanswered-icon.png') center center no-repeat; } } @@ -573,7 +573,7 @@ section.open-ended-child { display: inline-block; width: 25px; height: 20px; - background: url('../images/correct-icon.png') center center no-repeat; + background: url('#{$static-path}/images/correct-icon.png') center center no-repeat; } input { @@ -586,7 +586,7 @@ section.open-ended-child { display: inline-block; width: 20px; height: 20px; - background: url('../images/spinner.gif') center center no-repeat; + background: url('#{$static-path}/images/spinner.gif') center center no-repeat; } input { @@ -599,7 +599,7 @@ section.open-ended-child { display: inline-block; width: 20px; height: 20px; - background: url('../images/incorrect-icon.png') center center no-repeat; + background: url('#{$static-path}/images/incorrect-icon.png') center center no-repeat; text-indent: -9999px; } @@ -638,7 +638,7 @@ section.open-ended-child { top: 4px; width: 14px; height: 14px; - background: url('../images/unanswered-icon.png') center center no-repeat; + background: url('#{$static-path}/images/unanswered-icon.png') center center no-repeat; } &.processing, &.ui-icon-processing { @@ -647,7 +647,7 @@ section.open-ended-child { top: 6px; width: 25px; height: 20px; - background: url('../images/spinner.gif') center center no-repeat; + background: url('#{$static-path}/images/spinner.gif') center center no-repeat; } &.correct, &.ui-icon-check { @@ -656,7 +656,7 @@ section.open-ended-child { top: 6px; width: 25px; height: 20px; - background: url('../images/correct-icon.png') center center no-repeat; + background: url('#{$static-path}/images/correct-icon.png') center center no-repeat; } &.incorrect, &.ui-icon-close { @@ -665,7 +665,7 @@ section.open-ended-child { top: 6px; width: 20px; height: 20px; - background: url('../images/incorrect-icon.png') center center no-repeat; + background: url('#{$static-path}/images/incorrect-icon.png') center center no-repeat; } } @@ -703,7 +703,7 @@ section.open-ended-child { .grading { margin: 0 7px 0 0; padding-left: 25px; - background: url('../images/info-icon.png') left center no-repeat; + background: url('#{$static-path}/images/info-icon.png') left center no-repeat; text-indent: 0; } @@ -863,9 +863,9 @@ section.open-ended-child { margin-top: ($baseline/2); margin-bottom: ($baseline/2); padding: 25px; - border: 1px solid $error-red; + border: 1px solid $error-color; border-radius: 3px; - background-color: lighten($error-red, 25%); + background-color: lighten($error-color, 25%); font-size: 1em; } diff --git a/common/lib/xmodule/xmodule/css/problem/edit.scss b/common/lib/xmodule/xmodule/css/problem/edit.scss index 692985ec62..ac6f3f90ff 100644 --- a/common/lib/xmodule/xmodule/css/problem/edit.scss +++ b/common/lib/xmodule/xmodule/css/problem/edit.scss @@ -110,7 +110,7 @@ width: 26px; height: 21px; vertical-align: middle; - background: url(../images/problem-editor-icons.png) no-repeat; + background: url('#{$static-path}/images/problem-editor-icons.png') no-repeat; } .problem-editor-icon.heading1 { diff --git a/common/lib/xmodule/xmodule/css/video/display.scss b/common/lib/xmodule/xmodule/css/video/display.scss index 63d3a38844..5f531435bf 100644 --- a/common/lib/xmodule/xmodule/css/video/display.scss +++ b/common/lib/xmodule/xmodule/css/video/display.scss @@ -280,7 +280,7 @@ div.video { a.ui-slider-handle { @extend %ui-fake-link; @include transform(scale(.7, 1.3) translate3d(-80%, -15%, 0)); - background: $pink url(../images/slider-handle.png) center center no-repeat; + background: $pink url('#{$static-path}/images/slider-handle.png') center center no-repeat; background-size: 50%; border: 1px solid darken($pink, 20%); border-radius: 50%; @@ -314,7 +314,7 @@ div.video { .video_control { @extend %video-button; float: left; - background-image: url('../images/vcr.png'); + background-image: url('#{$static-path}/images/vcr.png'); background-position: 15px 15px ; background-repeat: no-repeat; border-left: none; @@ -445,7 +445,7 @@ div.video { div.speeds { &.is-opened { .speed-button { - background-image: url('../images/open-arrow.png'); + background-image: url('#{$static-path}/images/open-arrow.png'); } } @@ -460,7 +460,7 @@ div.video { .speed-button { @extend %video-button; @include clearfix(); - background-image: url('../images/closed-arrow.png'); + background-image: url('#{$static-path}/images/closed-arrow.png'); background-position: 10px center; background-repeat: no-repeat; min-width: 116px; @@ -515,14 +515,14 @@ div.video { &.is-muted { & > a { - background-image: url('../images/mute.png'); + background-image: url('#{$static-path}/images/mute.png'); } } & > a { @extend %video-button; @include clearfix(); - background-image: url('../images/volume.png'); + background-image: url('#{$static-path}/images/volume.png'); background-position: 10px center; background-repeat: no-repeat; width: 30px; @@ -559,7 +559,7 @@ div.video { a.ui-slider-handle { @extend %ui-fake-link; @include transition(height $tmg-s2 ease-in-out 0s, width $tmg-s2 ease-in-out 0s); - background: $pink url(../images/slider-handle.png) center center no-repeat; + background: $pink url('#{$static-path}/images/slider-handle.png') center center no-repeat; background-size: 50%; border: 1px solid darken($pink, 20%); border-radius: 15px; @@ -578,7 +578,7 @@ div.video { a.add-fullscreen { @extend %video-button; - background: url(../images/fullscreen.png) center no-repeat; + background: url('#{$static-path}/images/fullscreen.png') center no-repeat; border-left: none; float: left; padding: 0 11px; @@ -588,7 +588,7 @@ div.video { a.quality-control { @extend %video-button; - background: url(../images/hd.png) center no-repeat; + background: url('#{$static-path}/images/hd.png') center no-repeat; border-left: none; float: left; padding: 0 11px; @@ -610,7 +610,7 @@ div.video { @extend %video-button; @include transition(none); box-shadow: inset 1px 0 0 #555; - background: url('../images/cc.png') center no-repeat; + background: url('#{$static-path}/images/cc.png') center no-repeat; border-left: none; border-right: none; padding: 0 11px; diff --git a/common/lib/xmodule/xmodule/static_content.py b/common/lib/xmodule/xmodule/static_content.py index 3d58ac2350..deed93e0c5 100755 --- a/common/lib/xmodule/xmodule/static_content.py +++ b/common/lib/xmodule/xmodule/static_content.py @@ -95,10 +95,10 @@ def _write_styles(selector, output_root, classes): for class_ in classes: css_imports[class_].add(fragment_name) - module_styles_lines = [] - module_styles_lines.append("@import 'bourbon/bourbon';") - module_styles_lines.append("@import 'bourbon/addons/button';") - module_styles_lines.append("@import 'assets/anims';") + module_styles_lines = [ + "@import 'bourbon/bourbon';", + "@import 'base/variables';", + ] for class_, fragment_names in css_imports.items(): module_styles_lines.append("""{selector}.xmodule_{class_} {{""".format( class_=class_, selector=selector diff --git a/common/test/acceptance/pages/lms/learner_profile.py b/common/test/acceptance/pages/lms/learner_profile.py index 4b2f08f1eb..87325be1a7 100644 --- a/common/test/acceptance/pages/lms/learner_profile.py +++ b/common/test/acceptance/pages/lms/learner_profile.py @@ -175,7 +175,7 @@ class LearnerProfilePage(FieldsMixin, PageObject): """ self.wait_for_field('image') default_links = self.q(css='.image-frame').attrs('src') - return 'default-profile' in default_links[0] if default_links else False + return 'profiles/default' in default_links[0] if default_links else False def mouse_hover(self, element): """ diff --git a/conf/locale/babel_mako.cfg b/conf/locale/babel_mako.cfg index f017d73afa..b1722ade50 100644 --- a/conf/locale/babel_mako.cfg +++ b/conf/locale/babel_mako.cfg @@ -28,3 +28,5 @@ input_encoding = utf-8 input_encoding = utf-8 [mako: themes/**.html] input_encoding = utf-8 +[mako: themes/**.html] +input_encoding = utf-8 diff --git a/lms/djangoapps/branding/__init__.py b/lms/djangoapps/branding/__init__.py index e8fa35ee22..efe6c93876 100644 --- a/lms/djangoapps/branding/__init__.py +++ b/lms/djangoapps/branding/__init__.py @@ -70,4 +70,4 @@ def get_logo_url(): elif university: return staticfiles_storage.url('images/{uni}-on-edx-logo.png'.format(uni=university)) else: - return staticfiles_storage.url('images/default-theme/logo.png') + return staticfiles_storage.url('images/logo.png') diff --git a/lms/djangoapps/courseware/tests/test_comp_theming.py b/lms/djangoapps/courseware/tests/test_comp_theming.py new file mode 100644 index 0000000000..d2bc701238 --- /dev/null +++ b/lms/djangoapps/courseware/tests/test_comp_theming.py @@ -0,0 +1,73 @@ +"""Tests of comprehensive theming.""" + +from django.conf import settings +from django.test import TestCase + +from path import path # pylint: disable=no-name-in-module +import staticfiles + +from openedx.core.djangoapps.theming.test_util import with_comp_theme +from openedx.core.lib.tempdir import mkdtemp_clean + + +class TestComprehensiveTheming(TestCase): + """Test comprehensive theming.""" + + def setUp(self): + super(TestComprehensiveTheming, self).setUp() + + # Clear the internal staticfiles caches, to get test isolation. + staticfiles.finders._finders.clear() # pylint: disable=protected-access + + @with_comp_theme(settings.REPO_ROOT / 'themes/red-theme') + def test_red_footer(self): + resp = self.client.get('/') + self.assertEqual(resp.status_code, 200) + # This string comes from footer.html + self.assertContains(resp, "super-ugly") + # This string comes from header.html + self.assertContains(resp, "This file is only for demonstration, and is horrendous!") + + def test_theme_outside_repo(self): + # Need to create a temporary theme, and defer decorating the function + # until it is done, which leads to this strange nested-function style + # of test. + + # Make a temp directory as a theme. + 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("
TEMPORARY THEME
") + + @with_comp_theme(tmp_theme) + def do_the_test(self): + """A function to do the work so we can use the decorator.""" + resp = self.client.get('/') + self.assertEqual(resp.status_code, 200) + self.assertContains(resp, "TEMPORARY THEME") + + do_the_test(self) + + def test_theme_adjusts_staticfiles_search_path(self): + # Test that a theme adds itself to the staticfiles search path. + before_finders = list(settings.STATICFILES_FINDERS) + before_dirs = list(settings.STATICFILES_DIRS) + + @with_comp_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.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) + + def test_default_logo_image(self): + result = staticfiles.finders.find('images/logo.png') + self.assertEqual(result, settings.REPO_ROOT / 'lms/static/images/logo.png') + + @with_comp_theme(settings.REPO_ROOT / 'themes/red-theme') + def test_overridden_logo_image(self): + result = staticfiles.finders.find('images/logo.png') + self.assertEqual(result, settings.REPO_ROOT / 'themes/red-theme/lms/static/images/logo.png') diff --git a/lms/djangoapps/courseware/tests/test_footer.py b/lms/djangoapps/courseware/tests/test_footer.py index b662349138..0a14ee1860 100644 --- a/lms/djangoapps/courseware/tests/test_footer.py +++ b/lms/djangoapps/courseware/tests/test_footer.py @@ -3,13 +3,14 @@ Tests related to the basic footer-switching based off SITE_NAME to ensure edx.org uses an edx footer but other instances use an Open edX footer. """ -from mock import patch from nose.plugins.attrib import attr 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_is_edx_domain + @attr('shard_1') class TestFooter(TestCase): @@ -36,26 +37,26 @@ class TestFooter(TestCase): "youtube": "https://www.youtube.com/" } + @with_is_edx_domain(True) def test_edx_footer(self): """ Verify that the homepage, when accessed at edx.org, has the edX footer """ - with patch.dict('django.conf.settings.FEATURES', {"IS_EDX_DOMAIN": True}): - resp = self.client.get('/') - self.assertEqual(resp.status_code, 200) - self.assertContains(resp, 'footer-edx-v3') + resp = self.client.get('/') + 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 edx.org, has the Open edX footer """ - with patch.dict('django.conf.settings.FEATURES', {"IS_EDX_DOMAIN": False}): - resp = self.client.get('/') - self.assertEqual(resp.status_code, 200) - self.assertContains(resp, 'footer-openedx') + resp = self.client.get('/') + self.assertEqual(resp.status_code, 200) + self.assertContains(resp, 'footer-openedx') - @patch.dict(settings.FEATURES, {'IS_EDX_DOMAIN': True}) + @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/envs/aws.py b/lms/envs/aws.py index 9399a79e6c..7384e18aed 100644 --- a/lms/envs/aws.py +++ b/lms/envs/aws.py @@ -230,6 +230,7 @@ BULK_EMAIL_ROUTING_KEY_SMALL_JOBS = LOW_PRIORITY_QUEUE # Theme overrides THEME_NAME = ENV_TOKENS.get('THEME_NAME', None) +COMP_THEME_DIR = path(ENV_TOKENS.get('COMP_THEME_DIR', COMP_THEME_DIR)) # Marketing link overrides MKTG_URL_LINK_MAP.update(ENV_TOKENS.get('MKTG_URL_LINK_MAP', {})) @@ -679,10 +680,7 @@ PROFILE_IMAGE_BACKEND = ENV_TOKENS.get('PROFILE_IMAGE_BACKEND', PROFILE_IMAGE_BA PROFILE_IMAGE_SECRET_KEY = AUTH_TOKENS.get('PROFILE_IMAGE_SECRET_KEY', PROFILE_IMAGE_SECRET_KEY) PROFILE_IMAGE_MAX_BYTES = ENV_TOKENS.get('PROFILE_IMAGE_MAX_BYTES', PROFILE_IMAGE_MAX_BYTES) PROFILE_IMAGE_MIN_BYTES = ENV_TOKENS.get('PROFILE_IMAGE_MIN_BYTES', PROFILE_IMAGE_MIN_BYTES) -if FEATURES['IS_EDX_DOMAIN']: - PROFILE_IMAGE_DEFAULT_FILENAME = 'images/edx-theme/default-profile' -else: - PROFILE_IMAGE_DEFAULT_FILENAME = ENV_TOKENS.get('PROFILE_IMAGE_DEFAULT_FILENAME', PROFILE_IMAGE_DEFAULT_FILENAME) +PROFILE_IMAGE_DEFAULT_FILENAME = 'images/profiles/default' # EdxNotes config diff --git a/lms/envs/common.py b/lms/envs/common.py index 5d36d87ff5..57e8d28219 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -195,7 +195,7 @@ FEATURES = { # Enable URL that shows information about the status of variuous services 'ENABLE_SERVICE_STATUS': False, - # Toggle to indicate use of a custom theme + # Toggle to indicate use of the Stanford theming system 'USE_CUSTOM_THEME': False, # Don't autoplay videos for students @@ -451,6 +451,9 @@ COURSES_ROOT = ENV_ROOT / "data" DATA_DIR = COURSES_ROOT +# comprehensive theming system +COMP_THEME_DIR = "" + # TODO: Remove the rest of the sys.path modification here and in cms/envs/common.py sys.path.append(REPO_ROOT) sys.path.append(PROJECT_ROOT / 'djangoapps') @@ -1827,6 +1830,9 @@ INSTALLED_APPS = ( 'staticfiles', 'static_replace', + # Theming + 'openedx.core.djangoapps.theming', + # Our courseware 'circuit', 'courseware', @@ -2583,7 +2589,7 @@ PROFILE_IMAGE_BACKEND = { 'base_url': os.path.join(MEDIA_URL, 'profile-images/'), }, } -PROFILE_IMAGE_DEFAULT_FILENAME = 'images/default-theme/default-profile' +PROFILE_IMAGE_DEFAULT_FILENAME = 'images/profiles/default' PROFILE_IMAGE_DEFAULT_FILE_EXTENSION = 'png' # This secret key is used in generating unguessable URLs to users' # profile images. Once it has been set, changing it will make the diff --git a/lms/startup.py b/lms/startup.py index 809432a3ae..6869f90828 100644 --- a/lms/startup.py +++ b/lms/startup.py @@ -34,7 +34,7 @@ def run(): add_mimetypes() if settings.FEATURES.get('USE_CUSTOM_THEME', False): - enable_theme() + enable_stanford_theme() if settings.FEATURES.get('USE_MICROSITES', False): enable_microsites() @@ -69,7 +69,7 @@ def add_mimetypes(): mimetypes.add_type('application/font-woff', '.woff') -def enable_theme(): +def enable_stanford_theme(): """ Enable the settings for a custom theme, whose files should be stored in ENV_ROOT/themes/THEME_NAME (e.g., edx_all/themes/stanford). @@ -77,7 +77,7 @@ def enable_theme(): # Workaround for setting THEME_NAME to an empty # string which is the default due to this ansible # bug: https://github.com/ansible/ansible/issues/4812 - if settings.THEME_NAME == "": + if getattr(settings, "THEME_NAME", "") == "": settings.THEME_NAME = None return diff --git a/lms/static/certificates/sass/_components.scss b/lms/static/certificates/sass/_components.scss index 5333b6c660..3653fa4e23 100644 --- a/lms/static/certificates/sass/_components.scss +++ b/lms/static/certificates/sass/_components.scss @@ -494,7 +494,7 @@ } .accomplishment-rendering { - background: palette(grayscale, white-t) url('../images/bg-distinguished.png') center no-repeat; + background: palette(grayscale, white-t) url('#{$static-path}/images/bg-distinguished.png') center no-repeat; background-size: 65%; .deco-corner-tl { diff --git a/lms/static/images/default-theme/logo-large.png b/lms/static/images/logo-large.png similarity index 100% rename from lms/static/images/default-theme/logo-large.png rename to lms/static/images/logo-large.png diff --git a/lms/static/images/default-theme/logo.png b/lms/static/images/logo.png similarity index 100% rename from lms/static/images/default-theme/logo.png rename to lms/static/images/logo.png diff --git a/lms/static/images/default-theme/default-profile_120.png b/lms/static/images/profiles/default_120.png similarity index 100% rename from lms/static/images/default-theme/default-profile_120.png rename to lms/static/images/profiles/default_120.png diff --git a/lms/static/images/default-theme/default-profile_30.png b/lms/static/images/profiles/default_30.png similarity index 100% rename from lms/static/images/default-theme/default-profile_30.png rename to lms/static/images/profiles/default_30.png diff --git a/lms/static/images/default-theme/default-profile_50.png b/lms/static/images/profiles/default_50.png similarity index 100% rename from lms/static/images/default-theme/default-profile_50.png rename to lms/static/images/profiles/default_50.png diff --git a/lms/static/images/default-theme/default-profile_500.png b/lms/static/images/profiles/default_500.png similarity index 100% rename from lms/static/images/default-theme/default-profile_500.png rename to lms/static/images/profiles/default_500.png diff --git a/lms/static/sass/base/_base.scss b/lms/static/sass/base/_base.scss index 9e0520033a..e6d9665fe8 100644 --- a/lms/static/sass/base/_base.scss +++ b/lms/static/sass/base/_base.scss @@ -179,7 +179,7 @@ span.edx { width: 20px; height: 20px; margin-left: -($baseline/2); - background: url(../images/spinner.gif) no-repeat; + background: url('#{$static-path}/images/spinner.gif') no-repeat; } mark { @@ -205,7 +205,7 @@ mark { width: 27px; height: 24px; margin-right: ($baseline*0.75); - background: url(../images/large-white-error-icon.png) no-repeat; + background: url('#{$static-path}/images/large-white-error-icon.png') no-repeat; } .inner-wrapper { diff --git a/lms/static/sass/base/_extends.scss b/lms/static/sass/base/_extends.scss index 5d48734d8e..5c0f70b645 100644 --- a/lms/static/sass/base/_extends.scss +++ b/lms/static/sass/base/_extends.scss @@ -68,7 +68,7 @@ //Styles for Error messages %error-message-colors { - background: $error-red; + background: $error-color; border: 1px solid rgb(202, 17, 17); color: rgb(143, 14, 14); } diff --git a/lms/static/sass/base/_font_face.scss b/lms/static/sass/base/_font_face.scss index 25b6d1596e..b5beb9e854 100644 --- a/lms/static/sass/base/_font_face.scss +++ b/lms/static/sass/base/_font_face.scss @@ -3,9 +3,9 @@ @font-face { font-family: 'Open Sans'; src: - url('../fonts/OpenSans/OpenSans-Light-webfont.woff2') format('woff2'), - url('../fonts/OpenSans/OpenSans-Light-webfont.woff') format('woff'), - url('../fonts/OpenSans/OpenSans-Light-webfont.ttf') format('truetype'); + url('#{$static-path}/fonts/OpenSans/OpenSans-Light-webfont.woff2') format('woff2'), + url('#{$static-path}/fonts/OpenSans/OpenSans-Light-webfont.woff') format('woff'), + url('#{$static-path}/fonts/OpenSans/OpenSans-Light-webfont.ttf') format('truetype'); font-weight: 300; font-style: normal; } @@ -13,9 +13,9 @@ @font-face { font-family: 'Open Sans'; src: - url('../fonts/OpenSans/OpenSans-LightItalic-webfont.woff2') format('woff2'), - url('../fonts/OpenSans/OpenSans-LightItalic-webfont.woff') format('woff'), - url('../fonts/OpenSans/OpenSans-LightItalic-webfont.ttf') format('truetype'); + url('#{$static-path}/fonts/OpenSans/OpenSans-LightItalic-webfont.woff2') format('woff2'), + url('#{$static-path}/fonts/OpenSans/OpenSans-LightItalic-webfont.woff') format('woff'), + url('#{$static-path}/fonts/OpenSans/OpenSans-LightItalic-webfont.ttf') format('truetype'); font-weight: 300; font-style: italic; } @@ -23,9 +23,9 @@ @font-face { font-family: 'Open Sans'; src: - url('../fonts/OpenSans/OpenSans-Regular-webfont.woff2') format('woff2'), - url('../fonts/OpenSans/OpenSans-Regular-webfont.woff') format('woff'), - url('../fonts/OpenSans/OpenSans-Regular-webfont.ttf') format('truetype'); + url('#{$static-path}/fonts/OpenSans/OpenSans-Regular-webfont.woff2') format('woff2'), + url('#{$static-path}/fonts/OpenSans/OpenSans-Regular-webfont.woff') format('woff'), + url('#{$static-path}/fonts/OpenSans/OpenSans-Regular-webfont.ttf') format('truetype'); font-weight: 400; font-style: normal; } @@ -33,9 +33,9 @@ @font-face { font-family: 'Open Sans'; src: - url('../fonts/OpenSans/OpenSans-Italic-webfont.woff2') format('woff2'), - url('../fonts/OpenSans/OpenSans-Italic-webfont.woff') format('woff'), - url('../fonts/OpenSans/OpenSans-Italic-webfont.ttf') format('truetype'); + url('#{$static-path}/fonts/OpenSans/OpenSans-Italic-webfont.woff2') format('woff2'), + url('#{$static-path}/fonts/OpenSans/OpenSans-Italic-webfont.woff') format('woff'), + url('#{$static-path}/fonts/OpenSans/OpenSans-Italic-webfont.ttf') format('truetype'); font-weight: 400; font-style: italic; } @@ -43,9 +43,9 @@ @font-face { font-family: 'Open Sans'; src: - url('../fonts/OpenSans/OpenSans-Semibold-webfont.woff2') format('woff2'), - url('../fonts/OpenSans/OpenSans-Semibold-webfont.woff') format('woff'), - url('../fonts/OpenSans/OpenSans-Semibold-webfont.ttf') format('truetype'); + url('#{$static-path}/fonts/OpenSans/OpenSans-Semibold-webfont.woff2') format('woff2'), + url('#{$static-path}/fonts/OpenSans/OpenSans-Semibold-webfont.woff') format('woff'), + url('#{$static-path}/fonts/OpenSans/OpenSans-Semibold-webfont.ttf') format('truetype'); font-weight: 600; font-style: normal; } @@ -53,9 +53,9 @@ @font-face { font-family: 'Open Sans'; src: - url('../fonts/OpenSans/OpenSans-SemiboldItalic-webfont.woff2') format('woff2'), - url('../fonts/OpenSans/OpenSans-SemiboldItalic-webfont.woff') format('woff'), - url('../fonts/OpenSans/OpenSans-SemiboldItalic-webfont.ttf') format('truetype'); + url('#{$static-path}/fonts/OpenSans/OpenSans-SemiboldItalic-webfont.woff2') format('woff2'), + url('#{$static-path}/fonts/OpenSans/OpenSans-SemiboldItalic-webfont.woff') format('woff'), + url('#{$static-path}/fonts/OpenSans/OpenSans-SemiboldItalic-webfont.ttf') format('truetype'); font-weight: 600; font-style: italic; } @@ -63,9 +63,9 @@ @font-face { font-family: 'Open Sans'; src: - url('../fonts/OpenSans/OpenSans-Bold-webfont.woff2') format('woff2'), - url('../fonts/OpenSans/OpenSans-Bold-webfont.woff') format('woff'), - url('../fonts/OpenSans/OpenSans-Bold-webfont.ttf') format('truetype'); + url('#{$static-path}/fonts/OpenSans/OpenSans-Bold-webfont.woff2') format('woff2'), + url('#{$static-path}/fonts/OpenSans/OpenSans-Bold-webfont.woff') format('woff'), + url('#{$static-path}/fonts/OpenSans/OpenSans-Bold-webfont.ttf') format('truetype'); font-weight: 700; font-style: normal; } @@ -73,9 +73,9 @@ @font-face { font-family: 'Open Sans'; src: - url('../fonts/OpenSans/OpenSans-BoldItalic-webfont.woff2') format('woff2'), - url('../fonts/OpenSans/OpenSans-BoldItalic-webfont.woff') format('woff'), - url('../fonts/OpenSans/OpenSans-BoldItalic-webfont.ttf') format('truetype'); + url('#{$static-path}/fonts/OpenSans/OpenSans-BoldItalic-webfont.woff2') format('woff2'), + url('#{$static-path}/fonts/OpenSans/OpenSans-BoldItalic-webfont.woff') format('woff'), + url('#{$static-path}/fonts/OpenSans/OpenSans-BoldItalic-webfont.ttf') format('truetype'); font-weight: 700; font-style: italic; } diff --git a/lms/static/sass/base/_variables.scss b/lms/static/sass/base/_variables.scss index ebd490dd6a..6f139bec0b 100644 --- a/lms/static/sass/base/_variables.scss +++ b/lms/static/sass/base/_variables.scss @@ -1,89 +1,77 @@ // lms - utilities - variables // ==================== -// BASE -$baseline: 20px; +// #CONFIG: paths and configuration +// #UNITS: Basic units of measurement +// #GRID: Grid and layout variables +// #COLORS: Base, palette and theme color definitions + application +// #TYPOGRAPHY: Font definitions and scales +// #DEPTH: UI depth-based scale +// #SPACING: General UI spacing variables and scale +// #FORMS: form field/interaction variables +// #TIMING: Event timing variables +// #APPLICATIONS: Applying configuration +// #DEPRECATED: sunsetted and not to be used variables -// ==================== - -// LAYOUT: grid -$gw-column: 80px; -$gw-gutter: 20px; -$fg-column: $gw-column; -$fg-gutter: $gw-gutter; -$fg-max-columns: 12; -$fg-max-width: 1400px; -$fg-min-width: 810px; - -// ==================== - -// FONTS -$sans-serif: 'Open Sans', $verdana, sans-serif; -$monospace: Monaco, 'Bitstream Vera Sans Mono', 'Lucida Console', monospace; -$body-font-family: $sans-serif; -$serif: $georgia; - -// FONT-WEIGHTS -$font-light: 300; -$font-regular: 400; -$font-semibold: 600; -$font-bold: 700; - -// ==================== - -// MISC: base fonts/colors -$body-font-size: em(14); -$body-line-height: golden-ratio(.875em, 1); -$base-font-color: rgb(60,60,60); -$baseFontColor: rgb(60,60,60); -$base-font-color: rgb(60,60,60); -$lighter-base-font-color: rgb(100,100,100); -$very-light-text: rgb(255,255,255); // #ffffff - -// ==================== - -// 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; - -// ==================== - -// COLORS - utility -$transparent: rgba(0,0,0,0); // used when color value is needed for UI width/transitions but element is transparent - -// COLORS -$black: rgb(0,0,0); // #000000; -$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); // #FFFFFF; -$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(118,118,118); // #767676 AA compliant -$gray-l1: tint($gray,20%); // #989898 -$gray-l2: tint($gray,40%); // #b2b2b2 -$gray-l3: tint($gray,60%); // #cbcbcb -$gray-l4: tint($gray,80%); // #e5e5e5 -$gray-l5: tint($gray,90%); // #f2f2f2 -$gray-l6: tint($gray,95%); // #f8f8f8 -$gray-l7: tint($gray,99%); // #fdfdfd -$gray-d1: shade($gray,20%); // #656565 -$gray-d2: shade($gray,40%); // #4c4c4c -$gray-d3: shade($gray,60%); // #323232 -$gray-d4: shade($gray,80%); // #191919 +// ---------------------------- +// #CONFIG +// ---------------------------- +$static-path: '..' !default; -$pink: rgb(182,37,103); // #b72567; +// ---------------------------- +// #UNITS +// ---------------------------- +$baseline: 20px !default; + + +// ---------------------------- +// #GRID +// ---------------------------- +$gw-column: 80px !default; +$gw-gutter: 20px !default; +$fg-column: $gw-column !default; +$fg-gutter: $gw-gutter !default; +$fg-max-columns: 12 !default; +$fg-max-width: 1400px !default; +$fg-min-width: 810px !default; + + +// ---------------------------- +// #COLORS +// ---------------------------- +$transparent: rgba(0,0,0,0) !default; // used when color value is needed for UI width/transitions but element is transparent + +$black: rgb(0,0,0) !default; +$black-t0: rgba($black, 0.125) !default; +$black-t1: rgba($black, 0.25) !default; +$black-t2: rgba($black, 0.5) !default; +$black-t3: rgba($black, 0.75) !default; + +$white: rgb(255,255,255) !default; +$white-t0: rgba($white, 0.125) !default; +$white-t1: rgba($white, 0.25) !default; +$white-t2: rgba($white, 0.5) !default; +$white-t3: rgba($white, 0.75) !default; + +$gray: rgb(118,118,118) !default; +$gray-l1: tint($gray,20%) !default; +$gray-l2: tint($gray,40%) !default; +$gray-l3: tint($gray,60%) !default; +$gray-l4: tint($gray,80%) !default; +$gray-l5: tint($gray,90%) !default; +$gray-l6: tint($gray,95%) !default; +$gray-l7: tint($gray,99%) !default; +$gray-d1: shade($gray,20%) !default; +$gray-d2: shade($gray,40%) !default; +$gray-d3: shade($gray,60%) !default; +$gray-d4: shade($gray,80%) !default; + + +$blue: rgb(0, 120, 176); +$yellow: rgb(255, 252, 221); + +$pink: rgb(182,37,103); $pink-l1: tint($pink,20%); $pink-l2: tint($pink,40%); $pink-l3: tint($pink,60%); @@ -100,7 +88,7 @@ $pink-u1: desaturate($pink,15%); $pink-u2: desaturate($pink,30%); $pink-u3: desaturate($pink,45%); -$red: rgb(178, 6, 16); // #b20610; +$red: rgb(178, 6, 16); $red-l1: tint($red,20%); $red-l2: tint($red,40%); $red-l3: tint($red,60%); @@ -117,7 +105,7 @@ $red-u1: desaturate($red,15%); $red-u2: desaturate($red,30%); $red-u3: desaturate($red,45%); -$green: rgb(37, 184, 90); // #25b85a; +$green: rgb(37, 184, 90); $green-l1: tint($green,20%); $green-l2: tint($green,40%); $green-l3: tint($green,60%); @@ -198,84 +186,81 @@ $ui-notification-height: ($baseline*10); // ==================== -// COLORS: social platforms +// social platforms $twitter-blue: #55ACEE; $facebook-blue: #3B5998; $linkedin-blue: #0077B5; -// ==================== +// shadows +$shadow: rgba(0,0,0,0.2) !default; +$shadow-l1: rgba(0,0,0,0.1) !default; +$shadow-l2: rgba(0,0,0,0.05) !default; +$shadow-d1: rgba(0,0,0,0.4) !default; +$shadow-d2: rgba($black, 0.6) !default; -// TODO: both blue and yellow variables differ from CMS rgb value, need to confirm change to CMS variable is ok in current platform uses before switching. -//$yellow: rgb(255, 252, 221); +// system feedback-based colors +$error-color: rgb(253, 87, 87) !default; +$warning-color: rgb(181,42,103) !default; +$confirm-color: rgb(0, 136, 1) !default; +$active-color: $blue !default; +$highlight-color: rgb(255,255,0) !default; +$alert-color: rgb(212, 64, 64) !default; +$warning-color: rgb(237, 189, 60) !default; +$success-color: rgb(37, 184, 90) !default; -// ==================== +// newer color variables +$dark-gray1: rgb(74,74,74); +$light-gray1: rgb(242,242,242); +$light-gray2: rgb(171,171,171); +$light-gray3: rgb(249,249,249); +$dark-gray2: rgb(151,151,151); +$blue1: rgb(74,144,226); +$blue2: rgb(0,161,229); +$green1: rgb(97,161,46); +$red1: rgb(208,2,27); -// COLORS: old variables -// DEPRECATED: use colors in lists above -$error-red: rgb(253, 87, 87); -$danger-red: rgb(212, 64, 64); -$light-gray: rgb(221, 221, 221); -$dark-gray: rgb(51, 51, 51); -$border-color: rgb(200, 200, 200); -$sidebar-color: rgb(246, 246, 246); -$outer-border-color: rgb(170, 170, 170); -$light-gray: rgb(221,221,221); // #dddddd - -// ==================== - -// used by descriptor css -// DEPRECATED: use colors in lists above -$lightGrey: rgb(237,241,245); // #edf1f5 -$darkGrey: rgb(136,145,161); // #8891a1 -$lightGrey1: $gray-l3; -$blue-d1: shade($blue,20%); -$blue-d2: shade($blue,40%); -$blue-d4: shade($blue,80%); - -// ==================== - -// edx.org marketing site variables -$m-gray: rgb(138,140,143); // #8A8C8F -$m-gray-l1: rgb(151,153,155); // #97999B -$m-gray-l2: rgb(164,166,168); // #A4A6A8 -$m-gray-l3: rgb(177,178,180); // #B1B2B4 -$m-gray-l4: rgb(245,245,245); // #F5F5F5 -$m-gray-d1: rgb(111, 112, 116); // #6f7074 -$m-gray-d2: rgb(112,114,118); // #707276 -$m-gray-d3: rgb(100,102,104); // #646668 -$m-gray-d4: rgb(5,5,5); // #050505 +// edx-specific: marketing site variables +$m-gray: rgb(138,140,143); //C8F +$m-gray-l1: rgb(151,153,155); +$m-gray-l2: rgb(164,166,168); +$m-gray-l3: rgb(177,178,180); +$m-gray-l4: rgb(245,245,245); +$m-gray-d1: rgb(111, 112, 116); +$m-gray-d2: rgb(112,114,118); +$m-gray-d3: rgb(100,102,104); +$m-gray-d4: rgb(5,5,5); $m-gray-t0: rgba($m-gray,0.125); $m-gray-t1: rgba($m-gray,0.25); $m-gray-t2: rgba($m-gray,0.50); $m-gray-t3: rgba($m-gray,0.75); -$m-blue: rgb(26,161,222); // #1AA1DE -$m-blue-l1: rgb(43,172,230); // #2BACE6 -$m-blue-l2: rgb(66,181,233); // #42B5E9 -$m-blue-l3: rgb(89,190,236); // #59BEEC +$m-blue: rgb(26,161,222); +$m-blue-l1: rgb(43,172,230); +$m-blue-l2: rgb(66,181,233); +$m-blue-l3: rgb(89,190,236); $m-blue-l4: tint($m-blue,90%); $m-blue-l5: tint($m-blue,95%); $m-blue-l6: #4bb4fb; -$m-blue-d1: rgb(23,144,199); // #1790C7 +$m-blue-d1: rgb(23,144,199); $m-blue-d2: $blue; -$m-blue-d3: rgb(18,111,154); // #126F9A -$m-blue-d4: rgb(10,74,103); // #0A4A67 -$m-blue-d5: rgb(0,158,231); // #009EE7 +$m-blue-d3: rgb(18,111,154); +$m-blue-d4: rgb(10,74,103); +$m-blue-d5: rgb(0,158,231); $m-blue-d6: #256A97; $m-blue-t0: rgba($m-blue,0.125); $m-blue-t1: rgba($m-blue,0.25); $m-blue-t2: rgba($m-blue,0.50); $m-blue-t3: rgba($m-blue,0.75); -$m-pink: rgb(181,42,103); // #B52A67 -$m-pink-l1: rgb(202,47,115); //#CA2F73 -$m-pink-l2: rgb(211,63,128); //#D33F80 -$m-pink-l3: rgb(215,84,142); //#D7548E +$m-pink: rgb(181,42,103); +$m-pink-l1: rgb(202,47,115); +$m-pink-l2: rgb(211,63,128); +$m-pink-l3: rgb(215,84,142); $m-pink-l4: tint($m-pink,75%); $m-pink-l5: tint($m-pink,85%); -$m-pink-d1: rgb(160,37,91); // #A0255B -$m-pink-d2: rgb(140,32,79); // #8C204F -$m-pink-d3: rgb(119,28,68); // #771C44 +$m-pink-d1: rgb(160,37,91); +$m-pink-d2: rgb(140,32,79); +$m-pink-d3: rgb(119,28,68); $m-green: rgb(0, 136, 1); $m-green-s1: rgb(96, 188, 97); @@ -293,138 +278,100 @@ $m-green-t1: rgba($m-green,0.25); $m-green-t2: rgba($m-green,0.50); $m-green-t3: rgba($m-green,0.75); -// ==================== - -// SHADOWS -$shadow: rgba(0,0,0,0.2); -$shadow-l1: rgba(0,0,0,0.1); -$shadow-l2: rgba(0,0,0,0.05); -$shadow-d1: rgba(0,0,0,0.4); -$shadow-d2: rgba($black, 0.6); - -// ==================== - -$m-base-font-size: em(15); - -$base-font-color: rgb(60,60,60); -$baseFontColor: rgb(60,60,60); -$lighter-base-font-color: rgb(100,100,100); -$text-color: $dark-gray; - -$dark-trans-bg: rgba(0, 0, 0, .75); - -$body-bg: rgb(250,250,250); -$container-bg: $white; -$header-image: linear-gradient(-90deg, rgba(255,255,255, 1), rgba(230,230,230, 0.9)); -$header-bg: $white; -$courseware-header-image: linear-gradient(top, rgb(255,255,255), rgb(238,238,238)); -$courseware-header-bg: transparent; -$footer-bg: $white; -$courseware-footer-border: none; -$courseware-footer-shadow: none; -$courseware-footer-margin: 0px; -$courseware-border-bottom-color: rgb(68,162,222); // #44a2de -$courseware-button-border-color: rgb(230,230,230); // #e6e6e6 -$courseware-hover-color: rgb(51,52,53); // #333435 -$courseware-navigation-color: $blue; - -// ==================== - -// STATE: verified +// edx-specific: verified $verified-color-lvl1: $m-green; $verified-color-lvl2: $m-green-l1; $verified-color-lvl3: $m-green-l2; $verified-color-lvl4: $m-green-l3; $verified-color-lvl5: $m-green-l4; -// STATE: professional ed +// edx-specific: professional ed $professional-color-lvl1: $m-pink; $professional-color-lvl2: $m-pink-l1; $professional-color-lvl3: $m-pink-l2; $professional-color-lvl4: $m-pink-l3; $professional-color-lvl5: $m-pink-l4; -// STATE: honor code +// edx-specific: honor code $honorcode-color-lvl1: rgb(50, 165, 217); $honorcode-color-lvl2: tint($honorcode-color-lvl1, 33%); -// STATE: audit -$audit-color-lvl1: $light-gray; +// edx-specific: audit +$audit-color-lvl1: rgb(221, 221, 221); $audit-color-lvl2: tint($audit-color-lvl1, 33%); -// STATE: credit +// edx-specific: credit $credit-color-base: rgb(244,195,0); // accessible with black text -// ==================== -// ACTIONS: general -$button-bg-image: linear-gradient(rgb(255,255,255) 0%, rgb(250,250,250) 50%, rgb(237,237,237) 50%, rgb(220,220,220) 100%); -$button-bg-color: transparent; -$button-bg-hover-color: $white; +// ---------------------------- +// #TYPOGRAPHY +// ---------------------------- +$sans-serif: 'Open Sans', $verdana, sans-serif !default; +$monospace: Monaco, 'Bitstream Vera Sans Mono', 'Lucida Console', monospace !default; +$body-font-family: $sans-serif !default; +$serif: $georgia !default; -// ACTIONS: primary -$action-primary-bg: $m-blue-d3; -$action-primary-fg: $white; -$action-primary-shadow: $m-blue-d4; +// newer variables/standards +$f-serif: 'Bree Serif', Georgia, Cambria, 'Times New Roman', Times, serif !default; +$f-sans-serif: 'Open Sans','Helvetica Neue', Helvetica, Arial, sans-serif !default; +$f-monospace: 'Bitstream Vera Sans Mono', Consolas, Courier, monospace !default; -// ACTIONS: primary - focused - hover/active pseudo states -$action-primary-focused-bg: $m-blue-d1; -$action-primary-focused-fg: $white; +$font-light: 300 !default; +$font-regular: 400 !default; +$font-semibold: 600 !default; +$font-bold: 700 !default; -// ACTIONS: primary - current or active navigation item -$action-primary-active-bg: $m-blue; -$action-primary-active-fg: $m-blue-d3; -$action-primary-active-shadow: $m-blue-d2; -$action-primary-active-focused-fg: $m-blue-d4; -$action-primary-active-focused-shadow: $m-blue-d3; +$m-base-font-size: em(15) !default; -// ACTIONS: disabled -$action-primary-disabled-bg: $m-gray-d3; -$action-prmary-disabled-fg: $white; -// ACTIONS: secondary -$action-secondary-bg: $m-pink; -$action-secondary-fg: $white; -$action-secondary-shadow: $m-pink-d2; +// ---------------------------- +// #DEPTH +// ---------------------------- -// ACTIONS: secondary - focused - hover/active pseudo states -$action-secondary-focused-bg: $m-pink-l3; -$action-secondary-focused-fg: $white; -// ACTIONS: secondary - current or active navigation item -$action-secondary-active-bg: $m-pink-l2; -$action-secondary-active-fg: $m-pink-d1; -$action-secondary-active-shadow: $m-pink-d1; -$action-secondary-active-focused-fg: $m-pink-d3; -$action-secondary-active-focused-shadow: $m-pink-d2; +// ---------------------------- +// #TIMING +// ---------------------------- +$tmg-s3: 3.0s !default; +$tmg-s2: 2.0s !default; +$tmg-s1: 1.0s !default; +$tmg-avg: 0.75s !default; +$tmg-f1: 0.50s !default; +$tmg-f2: 0.25s !default; +$tmg-f3: 0.125s !default; -// ACTIONS: secondary - disabled -$action-secondary-disabled-bg: $m-gray-d3; -$action-secondary-disabled-fg: $white; -// HEADER: graphic-based page titles -$header-graphic-super-color: $m-blue-d1; -$header-graphic-sub-color: $m-gray-d2; +// ---------------------------- +// #FORMS +// ---------------------------- +$form-bg-color: $white !default; -// State-based colors -$error-color: $error-red; -$warning-color: $m-pink; -$confirm-color: $m-green; -$active-color: $blue; -$highlight-color: rgb(255,255,0); +// ---------------------------- +// #APPLICATIONS +// ---------------------------- +// font sizes +$body-font-size: em(14) !default; +$body-line-height: golden-ratio(.875em, 1) !default; -// Notifications -$notify-banner-bg-1: rgb(56,56,56); -$notify-banner-bg-2: rgb(136,136,136); -$notify-banner-bg-3: $shadow-l2; +$base-font-color: rgb(60,60,60) !default; +$baseFontColor: $base-font-color; -$alert-color: rgb(212, 64, 64); //rich red -$warning-color: rgb(237, 189, 60); //rich yellow -$success-color: rgb(37, 184, 90); //rich green +$lighter-base-font-color: rgb(100,100,100) $base-font-color; +$very-light-text: rgb(255,255,255) !default; +$text-color: rgb(51, 51, 51) !default; -// ==================== +// borders +$border-color-1: rgb(190,190,190) !default; +$border-color-2: rgb(200,200,200) !default; +$border-color-3: rgb(100,100,100) !default; +$border-color-4: rgb(252,252,252) !default; +$border-color-l1: $m-gray-l1 !default; +$border-color-l2: $m-gray-l2 !default; +$border-color-l3: $m-gray-l3 !default; +$border-color-l4: $m-gray-l4 !default; -// MISC: visual horizontal rules +// visual horizontal rules $faded-hr-image-1: linear-gradient(180deg, rgba(200,200,200, 0) 0%, rgba(200,200,200, 1) 50%, rgba(200,200,200, 0)); $faded-hr-image-2: linear-gradient(180deg, rgba(200,200,200, 0) 0%, rgba(200,200,200, 1)); $faded-hr-image-3: linear-gradient(180deg, rgba(200,200,200, 1) 0%, rgba(200,200,200, 0)); @@ -432,116 +379,147 @@ $faded-hr-image-4: linear-gradient(180deg, rgba(240,240,240, 0) 0%, rgba(240,240 $faded-hr-image-5: linear-gradient(180deg, rgba(255,255,255, 0) 0%, rgba(255,255,255, 0.8) 50%, rgba(255,255,255, 0)); $faded-hr-image-6: linear-gradient(90deg, rgba(255,255,255, 0) 0%, rgba(255,255,255, 0.6) 50%, rgba(255,255,255, 0)); -// MISC: dashboard -$dashboard-profile-header-image: linear-gradient(-90deg, rgb(255,255,255), rgb(245,245,245)); -$dashboard-profile-header-color: transparent; -$dashboard-profile-color: rgb(252,252,252); -$dot-color: $light-gray; -$dashboard-course-cover-border: $light-gray; +// notifications +$notify-banner-bg-1: rgb(56,56,56) !default; +$notify-banner-bg-2: rgb(136,136,136) !default; +$notify-banner-bg-3: $shadow-l2 !default; +$msg-bg: $m-blue-d3 !default; -// MISC: course assets -$content-wrapper-bg: $white; -$course-bg-color: #f2f2f2; -$course-bg-image: url(../images/bg-texture.png); -$account-content-wrapper-bg: shade($body-bg, 2%); -$course-profile-bg: rgb(245,245,245); -$course-header-bg: rgba(255,255,255, 0.93); +// actions +$button-bg-image: linear-gradient(rgb(255,255,255) 0%, rgb(250,250,250) 50%, rgb(237,237,237) 50%, rgb(220,220,220) 100%) !default; +$button-bg-color: transparent !default; +$button-bg-hover-color: $white !default; -// MISC: course background texture -//$course-bg-image: url(../images/bg-texture.png); +// action - primary +$action-primary-bg: $m-blue-d3 !default; +$action-primary-fg: $white !default; +$action-primary-shadow: $m-blue-d4 !default; +$action-primary-focused-bg: $m-blue-d1 !default; +$action-primary-focused-fg: $white !default; +$action-primary-active-bg: $m-blue !default; +$action-primary-active-fg: $m-blue-d3 !default; +$action-primary-active-shadow: $m-blue-d2 !default; +$action-primary-active-focused-fg: $m-blue-d4 !default; +$action-primary-active-focused-shadow: $m-blue-d3 !default; +$action-primary-disabled-bg: $m-gray-d3 !default; +$action-prmary-disabled-fg: $white !default; -// MISC: borders -$border-color-1: rgb(190,190,190); -$border-color-2: rgb(200,200,200); -$border-color-3: rgb(100,100,100); -$border-color-4: rgb(252,252,252); -$border-color-l1: $m-gray-l1; -$border-color-l2: $m-gray-l2; -$border-color-l3: $m-gray-l3; -$border-color-l4: $m-gray-l4; +// action - secondary +$action-secondary-bg: $m-pink !default; +$action-secondary-fg: $white !default; +$action-secondary-shadow: $m-pink-d2 !default; +$action-secondary-focused-bg: $m-pink-l3 !default; +$action-secondary-focused-fg: $white !default; +$action-secondary-active-bg: $m-pink-l2 !default; +$action-secondary-active-fg: $m-pink-d1 !default; +$action-secondary-active-shadow: $m-pink-d1 !default; +$action-secondary-active-focused-fg: $m-pink-d3 !default; +$action-secondary-active-focused-shadow: $m-pink-d2 !default; +$action-secondary-disabled-bg: $m-gray-d3 !default; +$action-secondary-disabled-fg: $white !default; -// MISC: links and buttons -$link-color: $blue; -$link-color-d1: $blue; -$link-hover: $blue-l1; // from our Pattern Library http://ux.edx.org/elements/colors/ -$site-status-color: $pink; -$button-color: $blue; -$button-archive-color: rgb(238,238,238); // #eeeeee +// actions - misc +$link-color: $blue !default; +$link-color-d1: $blue !default; +$link-hover: $blue-l1 !default; // from our Pattern Library http://ux.edx.org/elements/colors/ +$site-status-color: $pink !default; +$button-color: $blue !default; +$button-archive-color: rgb(238,238,238) !default; // #eeeeee -// MISC: shadow, form, modal -$shadow-color: $blue; -$form-bg-color: $white; -$modal-bg-color: rgb(245,245,245); +// larger, random elements +$dark-trans-bg: rgba(0, 0, 0, .75); -// MISC: sidebar +$body-bg: rgb(250,250,250) !default; + +$container-bg: $white !default; + +// header +$header-image: linear-gradient(-90deg, rgba(255,255,255, 1), rgba(230,230,230, 0.9)) !default; +$header-bg: $white !default; +$header-graphic-super-color: $m-blue-d1; +$header-graphic-sub-color: $m-gray-d2; +$header-sans-serif: 'Open Sans', Arial, Helvetica, sans-serif !default; +$header_image_margin: -69px !default; + +// footer +$footer-bg: $white !default; +$footer_margin: ($baseline/4) 0 ($baseline*1.5) 0 !default; + +// modal +$shadow-color: $blue !default; +$modal-bg-color: rgb(245,245,245) !default; + +// courseware elements +$courseware-header-image: linear-gradient(top, rgb(255,255,255), rgb(238,238,238)) !default; +$courseware-header-bg: transparent !default; +$courseware-footer-border: none !default; +$courseware-footer-shadow: none !default; +$courseware-footer-margin: 0px !default; +$courseware-border-bottom-color: rgb(68,162,222) !default; +$courseware-button-border-color: rgb(230,230,230) !default; +$courseware-hover-color: rgb(51,52,53) !default; +$courseware-navigation-color: $blue !default; + +// homepage, onboarding, and course discovery +$homepage__header--gradient__color--alpha: lighten($gray, 15%) !default; +$homepage__header--gradient__color--bravo: saturate($gray, 30%) !default; +$homepage__header--background: lighten($gray, 15%) !default; +$course-card-height: ($baseline*18) !default; +$course-image-height: ($baseline*8) !default; +$course-info-height: ($baseline*10) !default; +$course-title-height: ($baseline*3.6) !default; +$homepage-bg-image: none !default; +$login-banner-image: url('#{$static-path}/images/edx-theme/edx-background-banner-account.png') !default; +$register-banner-image: url('#{$static-path}/images/edx-theme/edx-background-banner-account.png') !default; +$passwordreset-banner-image: url('#{$static-path}/images/edx-theme/edx-background-banner-account.png') !default; + +$video-thumb-url: '#{$static-path}/images/homepage-hero-video-thumb.jpg' !default; + +// dashboard elements +$dashboard-profile-header-image: linear-gradient(-90deg, rgb(255,255,255), rgb(245,245,245)) !default; +$dashboard-profile-header-color: transparent !default; +$dashboard-profile-color: rgb(252,252,252) !default; +$dot-color: rgb(221, 221, 221) !default; +$dashboard-course-cover-border: rgb(221, 221, 221) !default; + +// course elements +$content-wrapper-bg: $white !default; +$course-bg-color: #f2f2f2 !default; +$course-bg-image: url('#{$static-path}/images/bg-texture.png') !default; +$account-content-wrapper-bg: shade($body-bg, 2%) !default; +$course-profile-bg: rgb(245,245,245) !default; +$course-header-bg: rgba(255,255,255, 0.93) !default; + +// search/listing results +$result-highlight-color-base: rgba($highlight-color, 0.25) !default; + +// sidebar $sidebar-chapter-bg-top: rgba(255, 255, 255, .5); $sidebar-chapter-bg-bottom: rgba(255, 255, 255, 0); -$sidebar-chapter-bg: rgb(246,246,246); // #f6f6f6 -$sidebar-active-image: linear-gradient(top, rgb(230,230,230), rgb(214,214,214)); - -// TOP HEADER IMAGE MARGIN -$header_image_margin: -69px; - -//FOOTER MARGIN -$footer_margin: ($baseline/4) 0 ($baseline*1.5) 0; - -// ==================== - -// VIEWS: homepage -$homepage__header--gradient__color--alpha: lighten($gray, 15%); -$homepage__header--gradient__color--bravo: saturate($gray, 30%); -$homepage__header--background: lighten($gray, 15%); - -// VIEWS: homepage and courses -$course-card-height: ($baseline*18); -$course-image-height: ($baseline*8); -$course-info-height: ($baseline*10); -$course-title-height: ($baseline*3.6); - -// ==================== - -// IMAGES: backgrounds -$homepage-bg-image: none; - -$login-banner-image: url(../images/edx-theme/edx-background-banner-account.png); -$register-banner-image: url(../images/edx-theme/edx-background-banner-account.png); -$passwordreset-banner-image: url(../images/edx-theme/edx-background-banner-account.png); - -$video-thumb-url: '../images/homepage-hero-video-thumb.jpg'; - -// ==================== - -// SPLINT: new standards - -// SPLINT: fonts -$f-serif: 'Bree Serif', Georgia, Cambria, 'Times New Roman', Times, serif; -$f-sans-serif: 'Open Sans','Helvetica Neue', Helvetica, Arial, sans-serif; -$f-monospace: 'Bitstream Vera Sans Mono', Consolas, Courier, monospace; - -// Header specific sans-serif -$header-sans-serif: 'Open Sans', Arial, Helvetica, sans-serif; - -// SPLINT: colors - -$msg-bg: $action-primary-bg; +$sidebar-chapter-bg: rgb(246,246,246) !default; +$sidebar-active-image: linear-gradient(top, rgb(230,230,230), rgb(214,214,214)) !default; -$dark-gray1: rgb(74,74,74); // # -$light-gray1: rgb(242,242,242); // #f2f2f2 -$light-gray2: rgb(171,171,171); // #ababab -$light-gray3: rgb(249,249,249); // #f9f9f9 -$dark-gray2: rgb(151,151,151); // #979797 -$blue1: rgb(74,144,226); // #4A90E2 -$blue2: rgb(0,161,229); // #00A1E5 -$green1: rgb(97,161,46); // #61A12E -$red1: rgb(208,2,27); // #D0021B +// student notes +$student-notes-highlight-color-base: saturate($yellow, 65%) !default; +$student-notes-highlight-color: tint($student-notes-highlight-color-base, 50%) !default; +$student-notes-highlight-color-focus: $student-notes-highlight-color-base !default; -// +case: search/result highlight -// -------------------- -$result-highlight-color-base: rgba($highlight-color, 0.25); +// ---------------------------- +// #DEPRECATED +// ---------------------------- +$danger-red: rgb(212, 64, 64) !default; +$light-gray: rgb(221, 221, 221) !default; +$dark-gray: rgb(51, 51, 51) !default; +$border-color: rgb(200, 200, 200) !default; +$sidebar-color: rgb(246, 246, 246) !default; +$outer-border-color: rgb(170, 170, 170); +$light-gray: rgb(221,221,221) !default; -// +feature: student notes -// -------------------- -$student-notes-highlight-color-base: saturate($yellow, 65%); -$student-notes-highlight-color: tint($student-notes-highlight-color-base, 50%); -$student-notes-highlight-color-focus: $student-notes-highlight-color-base; +// used by descriptor css +$lightGrey: rgb(237,241,245) !default; +$darkGrey: rgb(136,145,161) !default; +$lightGrey1: $gray-l3 !default; +$blue-d1: shade($blue,20%); +$blue-d2: shade($blue,40%); +$blue-d4: shade($blue,80%); diff --git a/lms/static/sass/course-rtl.scss b/lms/static/sass/course-rtl.scss index f34a72937a..9282339cdb 100644 --- a/lms/static/sass/course-rtl.scss +++ b/lms/static/sass/course-rtl.scss @@ -7,8 +7,8 @@ @import 'vendor/bi-app/bi-app-rtl'; // set the layout for right to left languages @import 'base/reset'; -@import 'base/font_face'; @import 'base/variables'; +@import 'base/font_face'; @import 'base/mixins'; diff --git a/lms/static/sass/course.scss b/lms/static/sass/course.scss index a265ddf8f3..fc666a978d 100644 --- a/lms/static/sass/course.scss +++ b/lms/static/sass/course.scss @@ -7,8 +7,8 @@ @import 'vendor/bi-app/bi-app-ltr'; // set the layout for left to right languages @import 'base/reset'; -@import 'base/font_face'; @import 'base/variables'; +@import 'base/font_face'; @import 'base/mixins'; diff --git a/lms/static/sass/course/_gradebook.scss b/lms/static/sass/course/_gradebook.scss index e211331871..9bd921dd6c 100644 --- a/lms/static/sass/course/_gradebook.scss +++ b/lms/static/sass/course/_gradebook.scss @@ -20,7 +20,7 @@ div.gradebook-wrapper { @include box-sizing(border-box); border-radius: 13px; border: 1px solid $table-border-color; - background: url(../images/search-icon.png) no-repeat 9px center $gray-l6; + background: url('#{$static-path}/images/search-icon.png') no-repeat 9px center $gray-l6; font-family: $sans-serif; font-size: 11px; box-shadow: 0 1px 4px rgba(0, 0, 0, .12) inset; diff --git a/lms/static/sass/course/_info.scss b/lms/static/sass/course/_info.scss index 6518d908f3..0bbca1d757 100644 --- a/lms/static/sass/course/_info.scss +++ b/lms/static/sass/course/_info.scss @@ -33,7 +33,7 @@ div.info-wrapper { h2 { font-size: $body-font-size; font-weight: bold; - background: url('../images/calendar-icon.png') 0 center no-repeat; + background: url('#{$static-path}/images/calendar-icon.png') 0 center no-repeat; padding-left: $baseline; } @@ -183,7 +183,7 @@ div.info-wrapper { } div.hitarea { - background-image: url('../images/treeview-default.gif') no-repeat; + background-image: url('#{$static-path}/images/treeview-default.gif') no-repeat; display: block; height: 100%; margin-left: 0; diff --git a/lms/static/sass/course/_textbook.scss b/lms/static/sass/course/_textbook.scss index b24a3a52a2..d2c3d3a048 100755 --- a/lms/static/sass/course/_textbook.scss +++ b/lms/static/sass/course/_textbook.scss @@ -72,7 +72,7 @@ div.book-wrapper { } div.hitarea { - background-image: url('../images/treeview-default.gif'); + background-image: url('#{$static-path}/images/treeview-default.gif'); position: relative; top: 4px; @@ -150,7 +150,7 @@ div.book-wrapper { left: 0; a { - background-image: url('../images/textbook/textbook-left.png'); + background-image: url('#{$static-path}/images/textbook/textbook-left.png'); } } @@ -158,7 +158,7 @@ div.book-wrapper { right: 0; a { - background-image: url('../images/textbook/textbook-right.png'); + background-image: url('#{$static-path}/images/textbook/textbook-right.png'); } } @@ -207,7 +207,7 @@ div.book-wrapper { padding: 0; a { - background-image: url('../images/slide-right-icon.png'); + background-image: url('#{$static-path}/images/slide-right-icon.png'); } h2 { diff --git a/lms/static/sass/course/base/_extends.scss b/lms/static/sass/course/base/_extends.scss index 55b46fb604..b309e677da 100644 --- a/lms/static/sass/course/base/_extends.scss +++ b/lms/static/sass/course/base/_extends.scss @@ -152,7 +152,7 @@ h1.top-header { position: relative; a { - background: #f6f6f6 url('../images/slide-left-icon.png') center center no-repeat; + background: #f6f6f6 url('#{$static-path}/images/slide-left-icon.png') center center no-repeat; border: 1px solid #D3D3D3; border-radius: 3px 0 0 3px; height: 16px; diff --git a/lms/static/sass/course/courseware/_amplifier.scss b/lms/static/sass/course/courseware/_amplifier.scss index 0abb8b261a..dea469df02 100644 --- a/lms/static/sass/course/courseware/_amplifier.scss +++ b/lms/static/sass/course/courseware/_amplifier.scss @@ -231,7 +231,7 @@ section.tool-wrapper { } .ui-slider-handle { - background: lighten( #586e75, 5% ) url('../images/amplifier-slider-handle.png') center no-repeat; + background: lighten( #586e75, 5% ) url('#{$static-path}/images/amplifier-slider-handle.png') center no-repeat; border: 1px solid darken(#002b36, 8%); box-shadow: inset 0 1px 0 lighten( #586e75, 20% ); margin-top: -.3em; diff --git a/lms/static/sass/course/courseware/_courseware.scss b/lms/static/sass/course/courseware/_courseware.scss index 06ac2a00b2..cb2509b0b4 100644 --- a/lms/static/sass/course/courseware/_courseware.scss +++ b/lms/static/sass/course/courseware/_courseware.scss @@ -614,7 +614,7 @@ div.course-wrapper { div.staff_actions { p.error { - color: $error-red + color: $error-color } p.success { color: $success-color; @@ -667,7 +667,7 @@ div.course-wrapper { header#open_close_accordion { a { - background-image: url('../images/slide-right-icon.png'); + background-image: url('#{$static-path}/images/slide-right-icon.png'); } } diff --git a/lms/static/sass/course/discussion/_form-wmd-toolbar.scss b/lms/static/sass/course/discussion/_form-wmd-toolbar.scss index 2caf6cd928..45db5c108a 100644 --- a/lms/static/sass/course/discussion/_form-wmd-toolbar.scss +++ b/lms/static/sass/course/discussion/_form-wmd-toolbar.scss @@ -52,7 +52,7 @@ margin-left: ($baseline/4); margin-right: ($baseline/4); position: absolute; - background-image: url(../images/wmd-buttons.png); + background-image: url('#{$static-path}/images/wmd-buttons.png'); background-repeat: no-repeat; background-position: 0px 0px; display: inline-block; diff --git a/lms/static/sass/course/instructor/_instructor_2.scss b/lms/static/sass/course/instructor/_instructor_2.scss index bef79f2556..0a29b01f2f 100644 --- a/lms/static/sass/course/instructor/_instructor_2.scss +++ b/lms/static/sass/course/instructor/_instructor_2.scss @@ -137,12 +137,12 @@ .request-response-error { margin: 0; padding-bottom: ($baseline); - color: $error-red; + color: $error-color; } .display-errors { line-height: 3em; - color: $error-red; + color: $error-color; } .slickgrid { diff --git a/lms/static/sass/course/modules/_calculator.scss b/lms/static/sass/course/modules/_calculator.scss index ee8e139526..311d2d3d54 100644 --- a/lms/static/sass/course/modules/_calculator.scss +++ b/lms/static/sass/course/modules/_calculator.scss @@ -17,7 +17,7 @@ .calc { @include transition(background-color $tmg-f2 ease-in-out 0s); - background: url("../images/calc-icon.png") $black-t1 no-repeat center; + background: url("#{$static-path}/images/calc-icon.png") $black-t1 no-repeat center; border-bottom: 0; color: $white; float: right; @@ -33,7 +33,7 @@ } &.closed { - background-image: url("../images/close-calc-icon.png"); + background-image: url("#{$static-path}/images/close-calc-icon.png"); background-color: $black; top: -36px; } @@ -155,7 +155,7 @@ height: 35px; width: 35px; border: none; - background: url("../images/info-icon.png") center center no-repeat; + background: url("#{$static-path}/images/info-icon.png") center center no-repeat; color: $white; &:focus { diff --git a/lms/static/sass/course/wiki/_wiki.scss b/lms/static/sass/course/wiki/_wiki.scss index 22347d7346..03ba78d48e 100644 --- a/lms/static/sass/course/wiki/_wiki.scss +++ b/lms/static/sass/course/wiki/_wiki.scss @@ -100,7 +100,7 @@ section.wiki { width: 180px; height: 27px; padding: 0 15px 0 35px; - background: url(../images/search-icon.png) no-repeat 9px center $gray-l6; + background: url('#{$static-path}/images/search-icon.png') no-repeat 9px center $gray-l6; border: 1px solid #c8c8c8; border-radius: 14px; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12) inset; diff --git a/lms/static/sass/discussion/_discussion.scss b/lms/static/sass/discussion/_discussion.scss index 09181d0012..b8894925d8 100644 --- a/lms/static/sass/discussion/_discussion.scss +++ b/lms/static/sass/discussion/_discussion.scss @@ -417,7 +417,7 @@ body.discussion { .discussion-article { position: relative; min-height: 500px; - background-image: url(../images/bg-texture.png); + background-image: url('#{$static-path}/images/bg-texture.png'); a { word-wrap: break-word; @@ -598,7 +598,7 @@ body.discussion { background: transparent; span { - background-image: url(../images/wmd-buttons-transparent.png); + background-image: url('#{$static-path}/images/wmd-buttons-transparent.png'); } } } @@ -720,7 +720,7 @@ body.discussion { } .loading-animation { - background-image: url(../images/spinner.gif); + background-image: url('#{$static-path}/images/spinner.gif'); } .discussion-show { @@ -742,7 +742,7 @@ body.discussion { @include margin-right(6px); width: 21px; height: 19px; - background: url(../images/show-hide-discussion-icon.png) no-repeat; + background: url('#{$static-path}/images/show-hide-discussion-icon.png') no-repeat; background-position: -21px 0; } } diff --git a/lms/static/sass/discussion/views/_create-edit-post.scss b/lms/static/sass/discussion/views/_create-edit-post.scss index 70c710b4a6..5260f4d79f 100644 --- a/lms/static/sass/discussion/views/_create-edit-post.scss +++ b/lms/static/sass/discussion/views/_create-edit-post.scss @@ -180,7 +180,7 @@ margin-bottom: $baseline; border-radius: 3px; padding: 0; - background: $error-red; + background: $error-color; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3) inset, 0 1px 0 rgba(255, 255, 255, .2); color: $white; list-style: none; @@ -188,7 +188,7 @@ .post-error { padding: ($baseline/2) $baseline 12px 45px; border-bottom: 1px solid $red; - background: url(../images/white-error-icon.png) no-repeat 15px 14px; + background: url('#{$static-path}/images/white-error-icon.png') no-repeat 15px 14px; &:last-child { border-bottom: none; diff --git a/lms/static/sass/elements/_creative-commons.scss b/lms/static/sass/elements/_creative-commons.scss index d14351f83b..dc3a5a0b05 100644 --- a/lms/static/sass/elements/_creative-commons.scss +++ b/lms/static/sass/elements/_creative-commons.scss @@ -1,10 +1,10 @@ @font-face { font-family: 'CreativeCommons'; - src: url('../fonts/CreativeCommons/cc.eot'); - src: url('../fonts/CreativeCommons/cc.eot#iefix') format('embedded-opentype'), - url('../fonts/CreativeCommons/cc.woff') format('woff'), - url('../fonts/CreativeCommons/cc.ttf') format('truetype'), - url('../fonts/CreativeCommons/cc.svg#CreativeCommons') format('svg'); + src: url('#{$static-path}/fonts/CreativeCommons/cc.eot'); + src: url('#{$static-path}/fonts/CreativeCommons/cc.eot#iefix') format('embedded-opentype'), + url('#{$static-path}/fonts/CreativeCommons/cc.woff') format('woff'), + url('#{$static-path}/fonts/CreativeCommons/cc.ttf') format('truetype'), + url('#{$static-path}/fonts/CreativeCommons/cc.svg#CreativeCommons') format('svg'); font-weight: normal; font-style: normal; } diff --git a/lms/static/sass/lms-course-rtl.scss b/lms/static/sass/lms-course-rtl.scss new file mode 100644 index 0000000000..47c0dcda71 --- /dev/null +++ b/lms/static/sass/lms-course-rtl.scss @@ -0,0 +1,13 @@ +@import 'bourbon/bourbon'; +@import 'vendor/bi-app/bi-app-rtl'; // set the layout for right to left languages + +@import 'base/reset'; +@import 'base/variables'; +@import 'base/font_face'; +@import 'base/mixins'; + +// This comment is used by preprocess_assets.py to include resources from a +// theme, for old-style deprecated theming. +// + +@import 'build-course'; // shared app style assets/rendering diff --git a/lms/static/sass/lms-course-rtl.scss.mako b/lms/static/sass/lms-course-rtl.scss.mako deleted file mode 100644 index 2e96d2e36f..0000000000 --- a/lms/static/sass/lms-course-rtl.scss.mako +++ /dev/null @@ -1,22 +0,0 @@ -@import 'bourbon/bourbon'; -@import 'vendor/bi-app/bi-app-rtl'; // set the layout for right to left languages - -@import 'base/reset'; -@import 'base/font_face'; -@import 'base/variables'; -@import 'base/mixins'; - -## THEMING -## ------- -## Set up this file to import an edX theme library if the environment -## indicates that a theme should be used. The assumption is that the -## theme resides outside of this main edX repository, in a directory -## called themes//, with its base Sass file in -## themes//static/sass/_.scss. That one entry -## point can be used to @import in as many other things as needed. -% if env["FEATURES"].get("USE_CUSTOM_THEME", False): - // import theme's Sass overrides - @import '${env.get('THEME_NAME')}'; -% endif - -@import 'build-course'; // shared app style assets/rendering diff --git a/lms/static/sass/lms-course.scss b/lms/static/sass/lms-course.scss new file mode 100644 index 0000000000..53ecb202e2 --- /dev/null +++ b/lms/static/sass/lms-course.scss @@ -0,0 +1,13 @@ +@import 'bourbon/bourbon'; +@import 'vendor/bi-app/bi-app-ltr'; // set the layout for left to right languages + +@import 'base/reset'; +@import 'base/variables'; +@import 'base/font_face'; +@import 'base/mixins'; + +// This comment is used by preprocess_assets.py to include resources from a +// theme, for old-style deprecated theming. +// + +@import 'build-course'; // shared app style assets/rendering diff --git a/lms/static/sass/lms-course.scss.mako b/lms/static/sass/lms-course.scss.mako deleted file mode 100644 index e4fbab34b3..0000000000 --- a/lms/static/sass/lms-course.scss.mako +++ /dev/null @@ -1,22 +0,0 @@ -@import 'bourbon/bourbon'; -@import 'vendor/bi-app/bi-app-ltr'; // set the layout for left to right languages - -@import 'base/reset'; -@import 'base/font_face'; -@import 'base/variables'; -@import 'base/mixins'; - -## THEMING -## ------- -## Set up this file to import an edX theme library if the environment -## indicates that a theme should be used. The assumption is that the -## theme resides outside of this main edX repository, in a directory -## called themes//, with its base Sass file in -## themes//static/sass/_.scss. That one entry -## point can be used to @import in as many other things as needed. -% if env["FEATURES"].get("USE_CUSTOM_THEME", False): - // import theme's Sass overrides - @import '${env.get('THEME_NAME')}'; -% endif - -@import 'build-course'; // shared app style assets/rendering diff --git a/lms/static/sass/lms-footer-rtl.scss b/lms/static/sass/lms-footer-rtl.scss new file mode 100644 index 0000000000..218aa1f964 --- /dev/null +++ b/lms/static/sass/lms-footer-rtl.scss @@ -0,0 +1,26 @@ +// Footer for OpenEdX (right-to-left) +// ================================== + +// libs and resets *do not edit* +@import 'bourbon/bourbon'; // lib - bourbon +@import 'vendor/bi-app/bi-app-rtl'; // set the layout for right to left languages + +// base - utilities +@import 'base/variables'; +@import 'base/mixins'; + +// This comment is used by preprocess_assets.py to include resources from a +// theme, for old-style deprecated theming. +// + +footer#footer-openedx { + @import 'base/reset'; + @import 'base/extends'; + @import 'base/base'; +} + +// base - elements +@import 'elements/typography'; + +// shared - platform +@import 'shared/footer'; diff --git a/lms/static/sass/lms-footer-rtl.scss.mako b/lms/static/sass/lms-footer-rtl.scss.mako deleted file mode 100644 index cc91fab379..0000000000 --- a/lms/static/sass/lms-footer-rtl.scss.mako +++ /dev/null @@ -1,35 +0,0 @@ -// Footer for OpenEdX (right-to-left) -// ================================== - -// libs and resets *do not edit* -@import 'bourbon/bourbon'; // lib - bourbon -@import 'vendor/bi-app/bi-app-rtl'; // set the layout for right to left languages - -// base - utilities -@import 'base/variables'; -@import 'base/mixins'; - -## THEMING -## ------- -## Set up this file to import an edX theme library if the environment -## indicates that a theme should be used. The assumption is that the -## theme resides outside of this main edX repository, in a directory -## called themes//, with its base Sass file in -## themes//static/sass/_.scss. That one entry -## point can be used to @import in as many other things as needed. -% if env["FEATURES"].get("USE_CUSTOM_THEME", False): - // import theme's Sass overrides - @import '${env.get('THEME_NAME')}'; -% endif - -footer#footer-openedx { - @import 'base/reset'; - @import 'base/extends'; - @import 'base/base'; -} - -// base - elements -@import 'elements/typography'; - -// shared - platform -@import 'shared/footer'; diff --git a/lms/static/sass/lms-footer.scss b/lms/static/sass/lms-footer.scss new file mode 100644 index 0000000000..ba292cc8e9 --- /dev/null +++ b/lms/static/sass/lms-footer.scss @@ -0,0 +1,26 @@ +// Footer for OpenEdX (left-to-right) +// ================================== + +// libs and resets *do not edit* +@import 'bourbon/bourbon'; // lib - bourbon +@import 'vendor/bi-app/bi-app-ltr'; // set the layout for left to right languages + +// base - utilities +@import 'base/variables'; +@import 'base/mixins'; + +// This comment is used by preprocess_assets.py to include resources from a +// theme, for old-style deprecated theming. +// + +footer#footer-openedx { + @import 'base/reset'; + @import 'base/extends'; + @import 'base/base'; +} + +// base - elements +@import 'elements/typography'; + +// shared - platform +@import 'shared/footer'; diff --git a/lms/static/sass/lms-footer.scss.mako b/lms/static/sass/lms-footer.scss.mako deleted file mode 100644 index 0b4aff0381..0000000000 --- a/lms/static/sass/lms-footer.scss.mako +++ /dev/null @@ -1,35 +0,0 @@ -// Footer for OpenEdX (left-to-right) -// ================================== - -// libs and resets *do not edit* -@import 'bourbon/bourbon'; // lib - bourbon -@import 'vendor/bi-app/bi-app-ltr'; // set the layout for left to right languages - -// base - utilities -@import 'base/variables'; -@import 'base/mixins'; - -## THEMING -## ------- -## Set up this file to import an edX theme library if the environment -## indicates that a theme should be used. The assumption is that the -## theme resides outside of this main edX repository, in a directory -## called themes//, with its base Sass file in -## themes//static/sass/_.scss. That one entry -## point can be used to @import in as many other things as needed. -% if env["FEATURES"].get("USE_CUSTOM_THEME", False): - // import theme's Sass overrides - @import '${env.get('THEME_NAME')}'; -% endif - -footer#footer-openedx { - @import 'base/reset'; - @import 'base/extends'; - @import 'base/base'; -} - -// base - elements -@import 'elements/typography'; - -// shared - platform -@import 'shared/footer'; diff --git a/lms/static/sass/lms-main-rtl.scss b/lms/static/sass/lms-main-rtl.scss new file mode 100644 index 0000000000..7ab3e3cd35 --- /dev/null +++ b/lms/static/sass/lms-main-rtl.scss @@ -0,0 +1,21 @@ +// lms - css application architecture +// ==================== + +// libs and resets *do not edit* +@import 'bourbon/bourbon'; // lib - bourbon +@import 'vendor/bi-app/bi-app-rtl'; // set the layout for right to left languages +@import 'base/variables-rtl'; + +// BASE *default edX offerings* +// ==================== + +// base - utilities +@import 'base/reset'; +@import 'base/variables'; +@import 'base/mixins'; + +// This comment is used by preprocess_assets.py to include resources from a +// theme, for old-style deprecated theming. +// + +@import 'build-lms'; // shared app style assets/rendering diff --git a/lms/static/sass/lms-main-rtl.scss.mako b/lms/static/sass/lms-main-rtl.scss.mako deleted file mode 100644 index dcb06ff91a..0000000000 --- a/lms/static/sass/lms-main-rtl.scss.mako +++ /dev/null @@ -1,30 +0,0 @@ -// lms - css application architecture -// ==================== - -// libs and resets *do not edit* -@import 'bourbon/bourbon'; // lib - bourbon -@import 'vendor/bi-app/bi-app-rtl'; // set the layout for right to left languages -@import 'base/variables-rtl'; - -// BASE *default edX offerings* -// ==================== - -// base - utilities -@import 'base/reset'; -@import 'base/variables'; -@import 'base/mixins'; - -## THEMING -## ------- -## Set up this file to import an edX theme library if the environment -## indicates that a theme should be used. The assumption is that the -## theme resides outside of this main edX repository, in a directory -## called themes//, with its base Sass file in -## themes//static/sass/_.scss. That one entry -## point can be used to @import in as many other things as needed. -% if env["FEATURES"].get("USE_CUSTOM_THEME", False): - // import theme's Sass overrides - @import '${env.get('THEME_NAME')}'; -% endif - -@import 'build-lms'; // shared app style assets/rendering diff --git a/lms/static/sass/lms-main.scss b/lms/static/sass/lms-main.scss new file mode 100644 index 0000000000..1a79ca0442 --- /dev/null +++ b/lms/static/sass/lms-main.scss @@ -0,0 +1,20 @@ +// lms - css application architecture +// ==================== + +// libs and resets *do not edit* +@import 'bourbon/bourbon'; // lib - bourbon +@import 'vendor/bi-app/bi-app-ltr'; // set the layout for left to right languages + +// BASE *default edX offerings* +// ==================== + +// base - utilities +@import 'base/reset'; +@import 'base/variables'; +@import 'base/mixins'; + +// This comment is used by preprocess_assets.py to include resources from a +// theme, for old-style deprecated theming. +// + +@import 'build-lms'; // shared app style assets/rendering diff --git a/lms/static/sass/lms-main.scss.mako b/lms/static/sass/lms-main.scss.mako deleted file mode 100644 index 83d7be23d3..0000000000 --- a/lms/static/sass/lms-main.scss.mako +++ /dev/null @@ -1,30 +0,0 @@ -// lms - css application architecture -// ==================== - -// libs and resets *do not edit* -@import 'bourbon/bourbon'; // lib - bourbon -@import 'vendor/bi-app/bi-app-ltr'; // set the layout for left to right languages -@import 'base/variables-ltr'; - -// BASE *default edX offerings* -// ==================== - -// base - utilities -@import 'base/reset'; -@import 'base/variables'; -@import 'base/mixins'; - -## THEMING -## ------- -## Set up this file to import an edX theme library if the environment -## indicates that a theme should be used. The assumption is that the -## theme resides outside of this main edX repository, in a directory -## called themes//, with its base Sass file in -## themes//static/sass/_.scss. That one entry -## point can be used to @import in as many other things as needed. -% if env["FEATURES"].get("USE_CUSTOM_THEME", False): - // import theme's Sass overrides - @import '${env.get('THEME_NAME')}'; -% endif - -@import 'build-lms'; // shared app style assets/rendering diff --git a/lms/static/sass/multicourse/_course_about.scss b/lms/static/sass/multicourse/_course_about.scss index d3c2f7d375..5027ccdc67 100644 --- a/lms/static/sass/multicourse/_course_about.scss +++ b/lms/static/sass/multicourse/_course_about.scss @@ -159,7 +159,7 @@ } #register_error { - background: $error-red; + background: $error-color; border: 1px solid rgb(202, 17, 17); color: rgb(143, 14, 14); display: none; @@ -405,7 +405,7 @@ .opencourseware { text-indent: -9999px; - background: url('../images/opencourseware.png') 0 0 no-repeat; + background: url('#{$static-path}/images/opencourseware.png') 0 0 no-repeat; width: 266px; height: 31px; margin-bottom: $baseline; @@ -419,7 +419,7 @@ li { list-style: none; padding-left: 29px; - background: url('../images/link-icon.png') left center no-repeat; + background: url('#{$static-path}/images/link-icon.png') left center no-repeat; } } } diff --git a/lms/static/sass/multicourse/_edge.scss b/lms/static/sass/multicourse/_edge.scss index 996825c7fd..1a04d367ef 100644 --- a/lms/static/sass/multicourse/_edge.scss +++ b/lms/static/sass/multicourse/_edge.scss @@ -175,7 +175,7 @@ $paleYellow: #fffcf1; width: 263px; height: 72px; margin: 150px auto 50px; - background: url(../images/edx-theme/edx-edge-logo-large.png) no-repeat; + background: url('#{$static-path}/images/edx-theme/edx-edge-logo-large.png') no-repeat; text-indent: -9999px; overflow: hidden; } diff --git a/lms/static/sass/views/_teams.scss b/lms/static/sass/views/_teams.scss index 688535cf0f..66959895fa 100644 --- a/lms/static/sass/views/_teams.scss +++ b/lms/static/sass/views/_teams.scss @@ -723,11 +723,11 @@ .u-field.error { input, textarea { - border-color: $danger-red; + border-color: $error-color; } .u-field-message-help, .u-field-description-message { - color: $danger-red !important; + color: $error-color !important; } } diff --git a/lms/static/sass/views/_verification.scss b/lms/static/sass/views/_verification.scss index bb4c32f847..1df2975e2c 100644 --- a/lms/static/sass/views/_verification.scss +++ b/lms/static/sass/views/_verification.scss @@ -1312,7 +1312,7 @@ display: block; width: 45px; height: 45px; - background: transparent url('../images/verified-ribbon.png') no-repeat 0 0; + background: transparent url('#{$static-path}/images/verified-ribbon.png') no-repeat 0 0; } .action-intro, .action-select { diff --git a/lms/static/themed_sass/README.rst b/lms/static/themed_sass/README.rst new file mode 100644 index 0000000000..1f2696c41e --- /dev/null +++ b/lms/static/themed_sass/README.rst @@ -0,0 +1,2 @@ +When USE_CUSTOM_THEME is true, .scss files are pre-processed to add imports of +the theme settings. This directory will hold the output of the pre-processing. diff --git a/lms/templates/courseware/course_about.html b/lms/templates/courseware/course_about.html index 870d4785ed..c8df28da92 100644 --- a/lms/templates/courseware/course_about.html +++ b/lms/templates/courseware/course_about.html @@ -17,7 +17,7 @@ from edxmako.shortcuts import marketing_link ga=microsite.get_value('google_analytics_file', 'theme-google-analytics.html') ) else: - google_analytics_file = '../google_analytics.html' + google_analytics_file = '../google-analytics.html' %> <%include file="${google_analytics_file}" /> diff --git a/lms/templates/google_analytics.html b/lms/templates/google-analytics.html similarity index 100% rename from lms/templates/google_analytics.html rename to lms/templates/google-analytics.html diff --git a/lms/templates/header.html b/lms/templates/header.html new file mode 100644 index 0000000000..b3732c3912 --- /dev/null +++ b/lms/templates/header.html @@ -0,0 +1,19 @@ +<%namespace name='static' file='static_content.html'/> +<%! +from microsite_configuration import microsite +%> +<% +theme_enabled = settings.FEATURES.get("USE_CUSTOM_THEME", False) +is_microsite = microsite.is_request_in_microsite() +style_overrides_file = microsite.get_value('css_overrides_file') +%> + +% if style_overrides_file: + +% endif + +% if theme_enabled and not is_microsite: + <%include file="theme-header.html" /> +% else: + <%include file="${microsite.get_template_path('navigation.html')}" /> +% endif diff --git a/lms/templates/main.html b/lms/templates/main.html index 0f9839ace7..e95328a25b 100644 --- a/lms/templates/main.html +++ b/lms/templates/main.html @@ -4,6 +4,7 @@ from django.core.urlresolvers import reverse from django.utils.http import urlquote_plus from django.utils.translation import ugettext as _ +from django.utils.translation import get_language_bidi from microsite_configuration import microsite from microsite_configuration import page_title_breadcrumbs from branding import api as branding_api @@ -62,7 +63,13 @@ from branding import api as branding_api <%static:css group='style-vendor'/> - <%static:css group='style-main'/> + ## route around the overwhelmingly complicated static:css nonsense + <% + application_css_path = "css/lms-main{rtl}.css".format( + rtl="-rtl" if get_language_bidi() else "", + ) + %> + % if disable_courseware_js: <%static:js group='base_vendor'/> @@ -85,30 +92,7 @@ from branding import api as branding_api <%block name="headextra"/> -<% - if theme_enabled() and not is_microsite(): - header_extra_file = 'theme-head-extra.html' - header_file = 'theme-header.html' - google_analytics_file = 'theme-google-analytics.html' - - style_overrides_file = None - - else: - header_extra_file = microsite.get_template_path('header_extra.html') - - if settings.FEATURES['IS_EDX_DOMAIN'] and not is_microsite(): - header_file = microsite.get_template_path('navigation-edx.html') - else: - header_file = microsite.get_template_path('navigation.html') - - google_analytics_file = microsite.get_template_path('google_analytics.html') - - style_overrides_file = microsite.get_value('css_overrides_file') -%> - - % if header_extra_file: - <%include file="${header_extra_file}" /> - % endif + <%static:optional_include_mako file="header-extra.html" with_microsite="True" /> <%include file="widgets/optimizely.html" /> <%include file="widgets/segment-io.html" /> @@ -116,11 +100,7 @@ from branding import api as branding_api - <%include file="${google_analytics_file}" /> - -% if style_overrides_file: - -% endif + <%static:optional_include_mako file="google-analytics.html" with_microsite="True" /> @@ -132,7 +112,7 @@ from branding import api as branding_api #content">${_("Skip to main content")} % if not disable_header: - <%include file="${header_file}" /> + <%include file="header.html" /> % endif
@@ -141,16 +121,7 @@ from branding import api as branding_api
% if not disable_footer: - <%block name="footer"> - ## Can be overridden by child templates wanting to hide the footer. - % if theme_enabled() and not is_microsite(): - <%include file="theme-footer.html" /> - % elif settings.FEATURES.get('IS_EDX_DOMAIN', False) and not is_microsite(): - <%include file="footer-edx-v3.html" /> - % else: - <%include file="${microsite.get_template_path('footer.html')}" /> - % endif - + <%include file="themable-footer.html" /> % endif % if not disable_window_wrap: diff --git a/lms/templates/themable-footer.html b/lms/templates/themable-footer.html new file mode 100644 index 0000000000..dc688d9f43 --- /dev/null +++ b/lms/templates/themable-footer.html @@ -0,0 +1,13 @@ +<%! +from microsite_configuration import microsite +%> +<% +theme_enabled = settings.FEATURES.get("USE_CUSTOM_THEME", False) +is_microsite = microsite.is_request_in_microsite() +%> + +% if theme_enabled and not is_microsite: + <%include file="theme-footer.html" /> +% else: + <%include file="${microsite.get_template_path('footer.html')}" /> +% endif diff --git a/openedx/core/djangoapps/theming/__init__.py b/openedx/core/djangoapps/theming/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/openedx/core/djangoapps/theming/core.py b/openedx/core/djangoapps/theming/core.py new file mode 100644 index 0000000000..4d114dc3ef --- /dev/null +++ b/openedx/core/djangoapps/theming/core.py @@ -0,0 +1,62 @@ +""" +Core logic for Comprehensive Theming. +""" + +from django.conf import settings + +import edxmako + + +def comprehensive_theme_changes(theme_dir): + """ + Calculate the set of changes needed to enable a comprehensive theme. + + Arguments: + theme_dir (path.path): the full path to the theming directory to use. + + Returns: + A dict indicating the changes to make: + + * 'settings': a dictionary of settings names and their new values. + + * 'mako_paths': a list of directories to prepend to the edxmako + template lookup path. + + """ + + changes = { + 'settings': {}, + 'mako_paths': [], + } + + templates_dir = theme_dir / "lms" / "templates" + if templates_dir.isdir(): + changes['settings']['TEMPLATE_DIRS'] = [templates_dir] + settings.TEMPLATE_DIRS + changes['mako_paths'].append(templates_dir) + + staticfiles_dir = theme_dir / "lms" / "static" + if staticfiles_dir.isdir(): + changes['settings']['STATICFILES_DIRS'] = [staticfiles_dir] + settings.STATICFILES_DIRS + + locale_dir = theme_dir / "lms" / "conf" / "locale" + if locale_dir.isdir(): + changes['settings']['LOCALE_PATHS'] = [locale_dir] + settings.LOCALE_PATHS + + favicon = theme_dir / "lms" / "static" / "images" / "favicon.ico" + if favicon.isfile(): + changes['settings']['FAVICON_PATH'] = str(favicon) + + return changes + + +def enable_comprehensive_theme(theme_dir): + """ + Add directories to relevant paths for comprehensive theming. + """ + changes = comprehensive_theme_changes(theme_dir) + + # Use the changes + for name, value in changes['settings'].iteritems(): + setattr(settings, name, value) + for template_dir in changes['mako_paths']: + edxmako.paths.add_lookup('main', template_dir, prepend=True) diff --git a/openedx/core/djangoapps/theming/startup.py b/openedx/core/djangoapps/theming/startup.py new file mode 100644 index 0000000000..ae217804b7 --- /dev/null +++ b/openedx/core/djangoapps/theming/startup.py @@ -0,0 +1,14 @@ +""" +Startup code for Comprehensive Theming +""" + +from path import Path as path +from django.conf import settings + +from .core import enable_comprehensive_theme + + +def run(): + """Enable comprehensive theming, if we should.""" + if settings.COMP_THEME_DIR: + enable_comprehensive_theme(theme_dir=path(settings.COMP_THEME_DIR)) diff --git a/openedx/core/djangoapps/theming/test_util.py b/openedx/core/djangoapps/theming/test_util.py new file mode 100644 index 0000000000..3a8dc011ac --- /dev/null +++ b/openedx/core/djangoapps/theming/test_util.py @@ -0,0 +1,89 @@ +""" +Test helpers for Comprehensive Theming. +""" + +from functools import wraps +import os +import os.path + +from mock import patch + +from django.conf import settings +from django.test.utils import override_settings + +import edxmako + +from .core import comprehensive_theme_changes + + +def with_comp_theme(theme_dir): + """ + A decorator to run a test with a particular comprehensive theme. + + Arguments: + theme_dir (str): the full path to the theme directory to use. + This will likely use `settings.REPO_ROOT` to get the full path. + + """ + # This decorator gets the settings changes needed for a theme, and applies + # them using the override_settings and edxmako.paths.add_lookup context + # managers. + + changes = comprehensive_theme_changes(theme_dir) + + def _decorator(func): # pylint: disable=missing-docstring + @wraps(func) + def _decorated(*args, **kwargs): # pylint: disable=missing-docstring + with override_settings(COMP_THEME_DIR=theme_dir, **changes['settings']): + with edxmako.save_lookups(): + for template_dir in changes['mako_paths']: + edxmako.paths.add_lookup('main', template_dir, prepend=True) + + return func(*args, **kwargs) + return _decorated + return _decorator + + +def with_is_edx_domain(is_edx_domain): + """ + A decorator to run a test as if IS_EDX_DOMAIN is true or false. + + We are transitioning away from IS_EDX_DOMAIN and are moving toward an edX + theme. This decorator changes both settings to let tests stay isolated + from the details. + + Arguments: + is_edx_domain (bool): are we an edX domain or not? + + """ + # This is weird, it's a decorator that conditionally applies other + # decorators, which is confusing. + def _decorator(func): # pylint: disable=missing-docstring + if is_edx_domain: + # This applies @with_comp_theme to the func. + func = with_comp_theme(settings.REPO_ROOT / "themes" / "edx.org")(func) + + # This applies @patch.dict() to the func to set IS_EDX_DOMAIN. + func = patch.dict('django.conf.settings.FEATURES', {"IS_EDX_DOMAIN": is_edx_domain})(func) + + return func + + return _decorator + + +def dump_theming_info(): + """Dump a bunch of theming information, for debugging.""" + for namespace, lookup in edxmako.LOOKUP.items(): + print "--- %s: %s" % (namespace, lookup.template_args['module_directory']) + for directory in lookup.directories: + print " %s" % (directory,) + + print "=" * 80 + for dirname, __, filenames in os.walk(settings.MAKO_MODULE_DIR): + print "%s ----------------" % (dir,) + for filename in sorted(filenames): + if filename.endswith(".pyc"): + continue + with open(os.path.join(dirname, filename)) as f: + content = len(f.read()) + print " %s: %d" % (filename, content) diff --git a/openedx/core/djangoapps/theming/tests/__init__.py b/openedx/core/djangoapps/theming/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pavelib/assets.py b/pavelib/assets.py index 3089cd786b..cabd6e515e 100644 --- a/pavelib/assets.py +++ b/pavelib/assets.py @@ -1,37 +1,64 @@ """ Asset compilation and collection. """ + from __future__ import print_function + import argparse +import glob +import traceback + from paver import tasks from paver.easy import sh, path, task, cmdopts, needs, consume_args, call_task, no_help from watchdog.observers import Observer from watchdog.events import PatternMatchingEventHandler -import glob -import traceback + from .utils.envs import Env from .utils.cmd import cmd, django_cmd # setup baseline paths COFFEE_DIRS = ['lms', 'cms', 'common'] -SASS_DIRS = { - "lms/static/sass": "lms/static/css", - "cms/static/sass": "cms/static/css", - "common/static/sass": "common/static/css", - "lms/static/certificates/sass": "lms/static/certificates/css", -} +# A list of directories. Each will be paired with a sibling /css directory. +SASS_DIRS = [ + path("lms/static/sass"), + path("lms/static/themed_sass"), + path("cms/static/sass"), + path("common/static/sass"), + path("lms/static/certificates/sass"), +] SASS_LOAD_PATHS = ['common/static', 'common/static/sass'] SASS_CACHE_PATH = '/tmp/sass-cache' -edxapp_env = Env() -if edxapp_env.feature_flags.get('USE_CUSTOM_THEME', False): - theme_name = edxapp_env.env_tokens.get('THEME_NAME', '') - parent_dir = path(edxapp_env.REPO_ROOT).abspath().parent - theme_root = parent_dir / "themes" / theme_name - COFFEE_DIRS.append(theme_root) - SASS_DIRS[theme_root / "static" / "sass"] = None +def configure_paths(): + """Configure our paths based on settings. Called immediately.""" + edxapp_env = Env() + if edxapp_env.feature_flags.get('USE_CUSTOM_THEME', False): + theme_name = edxapp_env.env_tokens.get('THEME_NAME', '') + parent_dir = path(edxapp_env.REPO_ROOT).abspath().parent + theme_root = parent_dir / "themes" / theme_name + COFFEE_DIRS.append(theme_root) + sass_dir = theme_root / "static" / "sass" + css_dir = theme_root / "static" / "css" + if sass_dir.isdir(): + css_dir.mkdir_p() + SASS_DIRS.append(sass_dir) + + if edxapp_env.env_tokens.get("COMP_THEME_DIR", ""): + theme_dir = path(edxapp_env.env_tokens["COMP_THEME_DIR"]) + lms_sass = theme_dir / "lms" / "static" / "sass" + lms_css = theme_dir / "lms" / "static" / "css" + if lms_sass.isdir(): + lms_css.mkdir_p() + SASS_DIRS.append(lms_sass) + studio_sass = theme_dir / "studio" / "static" / "sass" + studio_css = theme_dir / "studio" / "static" / "css" + if studio_sass.isdir(): + studio_css.mkdir_p() + SASS_DIRS.append(studio_sass) + +configure_paths() class CoffeeScriptWatcher(PatternMatchingEventHandler): @@ -71,7 +98,7 @@ class SassWatcher(PatternMatchingEventHandler): """ register files with observer """ - for dirname in SASS_LOAD_PATHS + SASS_DIRS.keys(): + for dirname in SASS_LOAD_PATHS + SASS_DIRS: paths = [] if '*' in dirname: paths.extend(glob.glob(dirname)) @@ -137,7 +164,7 @@ def coffeescript_files(): """ return find command for paths containing coffee files """ - dirs = " ".join([Env.REPO_ROOT / coffee_dir for coffee_dir in COFFEE_DIRS]) + dirs = " ".join(Env.REPO_ROOT / coffee_dir for coffee_dir in COFFEE_DIRS) return cmd('find', dirs, '-type f', '-name \"*.coffee\"') @@ -176,10 +203,11 @@ def compile_sass(options): if options.get('force'): parts.append("--force") parts.append("--load-path .") - for load_path in SASS_LOAD_PATHS + SASS_DIRS.keys(): + for load_path in SASS_LOAD_PATHS + SASS_DIRS: parts.append("--load-path {path}".format(path=load_path)) - for sass_dir, css_dir in SASS_DIRS.items(): + for sass_dir in SASS_DIRS: + css_dir = sass_dir.parent / "css" if css_dir: parts.append("{sass}:{css}".format(sass=sass_dir, css=css_dir)) else: @@ -197,7 +225,13 @@ def compile_templated_sass(systems, settings): `settings` is the Django settings module to use. """ for sys in systems: - sh(django_cmd(sys, settings, 'preprocess_assets')) + if sys == "studio": + sys = "cms" + sh(django_cmd( + sys, settings, 'preprocess_assets', + '{sys}/static/sass/*.scss'.format(sys=sys), + '{sys}/static/themed_sass'.format(sys=sys) + )) print("\t\tFinished preprocessing {} assets.".format(sys)) diff --git a/pavelib/paver_tests/test_servers.py b/pavelib/paver_tests/test_servers.py index 3dbf20388e..95c1c0ef69 100644 --- a/pavelib/paver_tests/test_servers.py +++ b/pavelib/paver_tests/test_servers.py @@ -12,14 +12,23 @@ EXPECTED_COFFEE_COMMAND = ( ) EXPECTED_SASS_COMMAND = ( "sass --update --cache-location /tmp/sass-cache --default-encoding utf-8 --style compressed" - " --quiet --load-path . --load-path common/static --load-path common/static/sass" - " --load-path lms/static/sass --load-path lms/static/certificates/sass" + " --quiet" + " --load-path ." + " --load-path common/static" + " --load-path common/static/sass" + " --load-path lms/static/sass" + " --load-path lms/static/themed_sass" " --load-path cms/static/sass --load-path common/static/sass" - " lms/static/sass:lms/static/css lms/static/certificates/sass:lms/static/certificates/css" - " cms/static/sass:cms/static/css common/static/sass:common/static/css" + " --load-path lms/static/certificates/sass" + " lms/static/sass:lms/static/css" + " lms/static/themed_sass:lms/static/css" + " cms/static/sass:cms/static/css" + " common/static/sass:common/static/css" + " lms/static/certificates/sass:lms/static/certificates/css" ) EXPECTED_PREPROCESS_ASSETS_COMMAND = ( "python manage.py {system} --settings={asset_settings} preprocess_assets" + " {system}/static/sass/*.scss {system}/static/themed_sass" ) EXPECTED_COLLECT_STATIC_COMMAND = ( "python manage.py {system} --settings={asset_settings} collectstatic --noinput > /dev/null" diff --git a/static/pattern-library/sass/utilities/_variables.scss b/static/pattern-library/sass/utilities/_variables.scss index ab31f9f0a7..5b308d94f4 100644 --- a/static/pattern-library/sass/utilities/_variables.scss +++ b/static/pattern-library/sass/utilities/_variables.scss @@ -17,6 +17,8 @@ // ---------------------------- $em-base: 16; // deliberately sets bourbon-based em-base (http://bourbon.io/docs/#em-base) +$static-path: '..' !default; + // ---------------------------- // #GRID @@ -133,52 +135,52 @@ $palettes: ( // typography: config @include font-face( 'Open Sans', - '../../fonts/OpenSans/OpenSans-Light-webfont', + '#{$static-path}/../fonts/OpenSans/OpenSans-Light-webfont', 300, $file-formats: woff woff2 ttf ); @include font-face( 'Open Sans', - '../../fonts/OpenSans/OpenSans-LightItalic-webfont', + '#{$static-path}/../fonts/OpenSans/OpenSans-LightItalic-webfont', 300, italic, $file-formats: woff woff2 ttf ); @include font-face( 'Open Sans', - '../../fonts/OpenSans/OpenSans-Regular-webfont', + '#{$static-path}/../fonts/OpenSans/OpenSans-Regular-webfont', 400, $file-formats: woff woff2 ttf ); @include font-face( 'Open Sans', - '../../fonts/OpenSans/OpenSans-Italic-webfont', + '#{$static-path}/../fonts/OpenSans/OpenSans-Italic-webfont', 400, italic, $file-formats: woff woff2 ttf ); @include font-face( 'Open Sans', - '../../fonts/OpenSans/OpenSans-Semibold-webfont', + '#{$static-path}/../fonts/OpenSans/OpenSans-Semibold-webfont', 600, $file-formats: woff woff2 ttf ); @include font-face( 'Open Sans', - '../../fonts/OpenSans/OpenSans-SemiboldItalic-webfont', + '#{$static-path}/../fonts/OpenSans/OpenSans-SemiboldItalic-webfont', 600, italic, $file-formats: woff woff2 ttf ); @include font-face( 'Open Sans', - '../../fonts/OpenSans/OpenSans-Bold-webfont', + '#{$static-path}/../fonts/OpenSans/OpenSans-Bold-webfont', 700, $file-formats: woff woff2 ttf ); @include font-face( 'Open Sans', - '../../fonts/OpenSans/OpenSans-BoldItalic-webfont', + '#{$static-path}/../fonts/OpenSans/OpenSans-BoldItalic-webfont', 700, italic, $file-formats: woff woff2 ttf diff --git a/themes/README.rst b/themes/README.rst new file mode 100644 index 0000000000..547c7d01f1 --- /dev/null +++ b/themes/README.rst @@ -0,0 +1,178 @@ +##################### +Comprehensive Theming +##################### + + +Comprehensive Theming lets you customize the appearance of your Open edX +installation. You can override Sass and CSS settings, images, or entire HTML +templates. + +Eventually, Comprehensive Theming will obsolete existing theming mechanisms, +but for now they co-exist peacefully. This document describes how to use +Comprehensive Theming, and also the changes you'll need to make to keep other +theming mechanisms working. + + +Creating a theme +================ + +A theme is a directory of assets. You can create this directory wherever you +like, it does not have to be inside the edx-platform directory. The structure +within this directory mirrors the assets in the edx-platform repo itself. +Files you provide in your theme are used in place of the same-named files in +edx-platform. Here's a sample:: + + my-theme + └── lms + ├── static + │   ├── images + │   │   └── logo.png + │   └── sass + │   ├── _overrides.scss + │   ├── lms-main-rtl.scss + │   └── lms-main.scss + └── templates + ├── footer.html + └── header.html + +The top directory is named whatever you like. This example uses "my-theme". +The files provided here override the files in edx-platform. In this case, the +``my-theme/lms/static/sass/lms-main.scss`` file is used in place of the +``edx-platform/lms/static/sass/lms-main.scss`` file. + + +Images +------ + +Images can be substituted simply by placing the new image at the right place +in the theme directory. In our example above, the lms/static/images/logo.png +image is overridden. + + +Sass/CSS +-------- + +Most CSS styling in Open edX is done with Sass files compiled to CSS. You can +override individual settings by creating a new Sass file that uses the existing +file, and overrides the few settings you want. + +For example, to change the fonts used throughout the site, you can create an +``lms/static/sass/_overrides.scss`` file with the change you want:: + + $sans-serif: 'Helvetica'; + +The variables that can currently be overridden are defined in +``lms/static/sass/base/_variables.scss``. + +**Note:** We are currently in the middle of a re-engineering of the Sass +variables. They will change in the future. If you are interested, you can see +the new development in the `edX Pattern Library`_. + +.. _edX Pattern Library: http://ux.edx.org/ + +Then create ``lms/static/sass/lms-main.scss`` to use those overrides, and also +the rest of the definitions from the original file:: + + // Our overrides for settings we want to change. + @import 'overrides'; + + // Import the original styles from edx-platform. + @import 'lms/static/sass/lms-main'; + +Do this for each .scss file your site needs. + + +HTML Templates +-------------- + +You can make changes to HTML templates by copying them to your theme directory +in the appropriate place, and making the changes you need. Keep in mind that +in the future if you upgrade the Open edX code, you may have to update the +copied template in your theme also. + +Template Names +============== + +Here are the list of template names that you *should* use in your comprehensive +theme (so far): + +* ``header.html`` +* ``footer.html`` + +You should **not** use the following names in your comprehensive theme: + +* ``themable-footer.html`` + +If you look at the ``main.html`` template file, you will notice that it includes +``header.html`` and ``themable-footer.html``, rather than ``footer.html``. +You might be inclined to override ``themable-footer.html`` as a result. DO NOT +DO THIS. ``themable-footer.html`` is an additional layer of indirection that +is necessary to avoid breaking microsites, which also refers to a file named +``footer.html``. The goal is to eventually make comprehensive theming do +everything that microsites does now, and then deprecate and remove microsites +from the codebase. At that point, the ``themable-footer.html`` file will go +away, since the additional layer of indirection will no longer be necessary. + +Installing your theme +--------------------- + +To use your theme, you need to add a configuration value pointing to your theme +directory. There are two ways to do this. + +#. If you usually edit server-vars.yml: + + #. As the vagrant user, edit (or create) + /edx/app/edx_ansible/server-vars.yml to add the + ``edxapp_comp_theme_dir`` value:: + + edxapp_comp_theme_dir: '/full/path/to/my-theme' + + #. Run the update script:: + + $ sudo /edx/bin/update configuration master + $ sudo /edx/bin/update edx-platform HEAD + +#. Otherwise, edit the /edx/app/edxapp/lms.env.json file to add the + ``COMP_THEME_DIR`` value:: + + "COMP_THEME_DIR": "/full/path/to/my-theme", + +Restart your site. Your changes should now be visible. + + +"Stanford" theming +================== + +If you want to continue using the "Stanford" theming system, there are a few +changes you'll need to make. + +Create the following new files in the ``sass`` directory of your theme: + +* lms-main.scss +* lms-main-rtl.scss +* lms-course.scss +* lms-course-rtl.scss +* lms-footer.scss +* lms-footer-rtl.scss + +The contents of each of these files will be very similar. Here's what +``lms-main.scss`` should look like:: + + $static-path: '../../../..'; + @import 'lms/static/sass/lms-main'; + @import '_default'; + +Each file should set the ``$static-path`` variable to a relative path that +points to the ``lms/static`` directory inside of ``edx-platform``. Then, +it should ``@import`` the sass file under ``lms/static/sass`` that matches +its name: ``lms-footer.scss`` should import ``lms/static/sass/lms-footer``, +for example. Finally, the file should import the ``_default`` name, which +refers to the ``_default.scss`` Sass file that should already exist in your +Stanford theme directory. + +If your theme uses a different name than "default", you'll need to use that +name in the ``@import`` line. + +Run the ``update_assets`` command to recompile the theme:: + + $ paver update_assets lms --settings=aws diff --git a/lms/static/images/edx-theme/edx-logo-bw.png b/themes/edx.org/lms/static/images/logo-large.png similarity index 100% rename from lms/static/images/edx-theme/edx-logo-bw.png rename to themes/edx.org/lms/static/images/logo-large.png diff --git a/lms/static/images/edx-theme/edx-header-logo.png b/themes/edx.org/lms/static/images/logo.png similarity index 100% rename from lms/static/images/edx-theme/edx-header-logo.png rename to themes/edx.org/lms/static/images/logo.png diff --git a/lms/static/images/edx-theme/default-profile_120.png b/themes/edx.org/lms/static/images/profiles/default_120.png similarity index 100% rename from lms/static/images/edx-theme/default-profile_120.png rename to themes/edx.org/lms/static/images/profiles/default_120.png diff --git a/lms/static/images/edx-theme/default-profile_30.png b/themes/edx.org/lms/static/images/profiles/default_30.png similarity index 100% rename from lms/static/images/edx-theme/default-profile_30.png rename to themes/edx.org/lms/static/images/profiles/default_30.png diff --git a/lms/static/images/edx-theme/default-profile_50.png b/themes/edx.org/lms/static/images/profiles/default_50.png similarity index 100% rename from lms/static/images/edx-theme/default-profile_50.png rename to themes/edx.org/lms/static/images/profiles/default_50.png diff --git a/lms/static/images/edx-theme/default-profile_500.png b/themes/edx.org/lms/static/images/profiles/default_500.png similarity index 100% rename from lms/static/images/edx-theme/default-profile_500.png rename to themes/edx.org/lms/static/images/profiles/default_500.png diff --git a/themes/edx.org/lms/templates/footer.html b/themes/edx.org/lms/templates/footer.html new file mode 100644 index 0000000000..d74638c628 --- /dev/null +++ b/themes/edx.org/lms/templates/footer.html @@ -0,0 +1,81 @@ +## mako +<%! + from django.utils.translation import ugettext as _ + from branding.api import get_footer +%> +<% footer = get_footer(is_secure=is_secure) %> +<%namespace name='static' file='static_content.html'/> + +## WARNING: These files are specific to edx.org and are not used in installations outside of that domain. Open edX users will want to use the file "footer.html" for any changes or overrides. +
+ + +
+% if include_dependencies: + <%static:js group='base_vendor'/> + <%static:css group='style-vendor'/> + <%include file="widgets/segment-io.html" /> +% endif +% if footer_css_urls: + % for url in footer_css_urls: + + % endfor +% endif +% if footer_js_url: + +% endif + diff --git a/themes/edx.org/lms/templates/header.html b/themes/edx.org/lms/templates/header.html new file mode 100644 index 0000000000..3194120dc9 --- /dev/null +++ b/themes/edx.org/lms/templates/header.html @@ -0,0 +1,157 @@ +## mako +<%namespace name='static' file='static_content.html'/> +<%namespace file='main.html' import="login_query"/> +<%! +from django.core.urlresolvers import reverse +from django.utils.translation import ugettext as _ + +from microsite_configuration import microsite +from microsite_configuration.templatetags.microsite import platform_name + +# App that handles subdomain specific branding +import branding +# app that handles site status messages +from status.status import get_site_status_msg +%> + +## Provide a hook for themes to inject branding on top. +<%block name="navigation_top" /> + +<%block> +<% +try: + course_id = course.id.to_deprecated_string() +except: + # can't figure out a better way to get at a possibly-defined course var + course_id = None +site_status_msg = get_site_status_msg(course_id) +%> +% if site_status_msg: +
+
+ +

${site_status_msg}

+
+
+% endif + + + +% if course: + +% endif + +%if not user.is_authenticated(): + <%include file="forgot_password_modal.html" /> +%endif + +<%include file="help_modal.html"/> diff --git a/themes/red-theme/lms/static/css/README.txt b/themes/red-theme/lms/static/css/README.txt new file mode 100644 index 0000000000..5af45a252e --- /dev/null +++ b/themes/red-theme/lms/static/css/README.txt @@ -0,0 +1,3 @@ +CSS files should go in this directory. If you are using a CSS preprocessor +like Sass, you should configure it to output CSS in this directory. Running +`paver compile_sass` should do the right thing in this case. diff --git a/themes/red-theme/lms/static/images/logo.png b/themes/red-theme/lms/static/images/logo.png new file mode 100644 index 0000000000..803bdfbaec Binary files /dev/null and b/themes/red-theme/lms/static/images/logo.png differ diff --git a/themes/red-theme/lms/static/sass/_overrides.scss b/themes/red-theme/lms/static/sass/_overrides.scss new file mode 100755 index 0000000000..4e5e1f2b6e --- /dev/null +++ b/themes/red-theme/lms/static/sass/_overrides.scss @@ -0,0 +1,7 @@ +// Theming overrides for sample theme +$header-bg: rgb(250,0,0); +$footer-bg: rgb(250,0,0); +$container-bg: rgb(250,0,0); +$content-wrapper-bg: rgb(250,0,0); +$serif: 'Comic Sans', 'Comic Sans MS'; +$sans-serif: 'Comic Sans', 'Comic Sans MS'; diff --git a/themes/red-theme/lms/static/sass/lms-main-rtl.scss b/themes/red-theme/lms/static/sass/lms-main-rtl.scss new file mode 100755 index 0000000000..3eaad226a2 --- /dev/null +++ b/themes/red-theme/lms/static/sass/lms-main-rtl.scss @@ -0,0 +1,5 @@ +// Theming overrides for sample theme +@import 'overrides'; + +// import the rest of the application +@import 'lms/static/sass/lms-main-rtl'; diff --git a/themes/red-theme/lms/static/sass/lms-main.scss b/themes/red-theme/lms/static/sass/lms-main.scss new file mode 100755 index 0000000000..d6287e8215 --- /dev/null +++ b/themes/red-theme/lms/static/sass/lms-main.scss @@ -0,0 +1,5 @@ +// Theming overrides for sample theme +@import 'overrides'; + +// import the rest of the application +@import 'lms/static/sass/lms-main'; diff --git a/themes/red-theme/lms/templates/footer.html b/themes/red-theme/lms/templates/footer.html new file mode 100755 index 0000000000..e87eb8f06f --- /dev/null +++ b/themes/red-theme/lms/templates/footer.html @@ -0,0 +1,111 @@ +## mako +<%namespace name='static' file='static_content.html'/> +<%! +from django.core.urlresolvers import reverse +from django.utils.translation import ugettext as _ +from microsite_configuration.templatetags.microsite import platform_name +%> + + diff --git a/themes/red-theme/lms/templates/header.html b/themes/red-theme/lms/templates/header.html new file mode 100755 index 0000000000..d4b1caaada --- /dev/null +++ b/themes/red-theme/lms/templates/header.html @@ -0,0 +1,164 @@ +## mako +<%namespace name='static' file='static_content.html'/> +<%namespace file='main.html' import="login_query, stanford_theme_enabled"/> +<%! +from django.core.urlresolvers import reverse +from django.utils.translation import ugettext as _ + +# App that handles subdomain specific branding +import branding +# app that handles site status messages +from status.status import get_site_status_msg + +from microsite_configuration import microsite +from microsite_configuration.templatetags.microsite import platform_name +from ccx.overrides import get_current_ccx +%> + +## Provide a hook for themes to inject branding on top. +<%block name="navigation_top" /> + +<%block> +<% +try: + course_id = course.id.to_deprecated_string() +except: + # can't figure out a better way to get at a possibly-defined course var + course_id = None +site_status_msg = get_site_status_msg(course_id) +%> +% if site_status_msg: +
+
+ +

${site_status_msg}

+
+
+% endif + + + +% if course: + +% endif + +%if not user.is_authenticated(): + <%include file="forgot_password_modal.html" /> +%endif + +<%include file="help_modal.html"/>