Comprehensive theming
This is a squash of 38 commits ending with 5b080f979d692804452400ac5bed9b17c50b001e
16
.gitignore
vendored
@@ -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/
|
||||
|
||||
@@ -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.
|
||||
@@ -754,6 +754,9 @@ INSTALLED_APPS = (
|
||||
'static_replace',
|
||||
'require',
|
||||
|
||||
# Theming
|
||||
'openedx.core.djangoapps.theming',
|
||||
|
||||
# comment common
|
||||
'django_comment_common',
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
// * +Timing
|
||||
// * +Archetype UI
|
||||
// * +Specific UI
|
||||
// * +Paths
|
||||
// * +Deprecated
|
||||
|
||||
$baseline: 20px;
|
||||
@@ -213,6 +214,10 @@ $ui-link-color-focus: $blue-s1;
|
||||
$ui-notification-height: ($baseline*10);
|
||||
$ui-update-color: $blue-l4;
|
||||
|
||||
// +Paths
|
||||
// ====================
|
||||
$static-path: '..' !default;
|
||||
|
||||
// +Deprecated
|
||||
// ====================
|
||||
// do not use, future clean up will use updated styles
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -12,11 +12,7 @@
|
||||
|
||||
<div class="wrapper wrapper-l">
|
||||
<h1 class="branding"><a href="/">
|
||||
% if settings.FEATURES.get('IS_EDX_DOMAIN', False):
|
||||
<img src="${static.url("images/edx-theme/edx-studio-logo.png")}" alt="${settings.STUDIO_NAME}" />
|
||||
% else:
|
||||
<img src="${static.url("images/default-theme/logo.png")}" alt="${settings.STUDIO_NAME}" />
|
||||
% endif
|
||||
<img src="${static.url("images/header-logo.png")}" alt="${settings.STUDIO_NAME}" />
|
||||
</a></h1>
|
||||
|
||||
% if context_course:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = 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 = content.replace(
|
||||
"//<THEME-OVERRIDE>",
|
||||
"@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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%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:
|
||||
content.write(tmpl.render_context(context))
|
||||
%></%def>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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):
|
||||
"""
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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')
|
||||
|
||||
73
lms/djangoapps/courseware/tests/test_comp_theming.py
Normal file
@@ -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("<footer>TEMPORARY THEME</footer>")
|
||||
|
||||
@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')
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 = None
|
||||
|
||||
# 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')
|
||||
@@ -1832,6 +1835,9 @@ INSTALLED_APPS = (
|
||||
'staticfiles',
|
||||
'static_replace',
|
||||
|
||||
# Theming
|
||||
'openedx.core.djangoapps.theming',
|
||||
|
||||
# Our courseware
|
||||
'circuit',
|
||||
'courseware',
|
||||
@@ -2588,7 +2594,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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 576 B After Width: | Height: | Size: 576 B |
|
Before Width: | Height: | Size: 1023 B After Width: | Height: | Size: 1023 B |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,11 @@ $baseline: 20px;
|
||||
|
||||
// ====================
|
||||
|
||||
// PATH
|
||||
$static-path: '..' !default;
|
||||
|
||||
// ====================
|
||||
|
||||
// LAYOUT: grid
|
||||
$gw-column: 80px;
|
||||
$gw-gutter: 20px;
|
||||
@@ -18,10 +23,10 @@ $fg-min-width: 810px;
|
||||
// ====================
|
||||
|
||||
// FONTS
|
||||
$sans-serif: 'Open Sans', $verdana, sans-serif;
|
||||
$monospace: Monaco, 'Bitstream Vera Sans Mono', 'Lucida Console', monospace;
|
||||
$sans-serif: 'Open Sans', $verdana, sans-serif !default;
|
||||
$monospace: Monaco, 'Bitstream Vera Sans Mono', 'Lucida Console', monospace !default;
|
||||
$body-font-family: $sans-serif;
|
||||
$serif: $georgia;
|
||||
$serif: $georgia !default;
|
||||
|
||||
// FONT-WEIGHTS
|
||||
$font-light: 300;
|
||||
@@ -313,13 +318,13 @@ $text-color: $dark-gray;
|
||||
|
||||
$dark-trans-bg: rgba(0, 0, 0, .75);
|
||||
|
||||
$body-bg: rgb(250,250,250);
|
||||
$container-bg: $white;
|
||||
$body-bg: rgb(250,250,250) !default;
|
||||
$container-bg: $white !default;
|
||||
$header-image: linear-gradient(-90deg, rgba(255,255,255, 1), rgba(230,230,230, 0.9));
|
||||
$header-bg: $white;
|
||||
$header-bg: $white !default;
|
||||
$courseware-header-image: linear-gradient(top, rgb(255,255,255), rgb(238,238,238));
|
||||
$courseware-header-bg: transparent;
|
||||
$footer-bg: $white;
|
||||
$footer-bg: $white !default;
|
||||
$courseware-footer-border: none;
|
||||
$courseware-footer-shadow: none;
|
||||
$courseware-footer-margin: 0px;
|
||||
@@ -440,15 +445,15 @@ $dot-color: $light-gray;
|
||||
$dashboard-course-cover-border: $light-gray;
|
||||
|
||||
// MISC: course assets
|
||||
$content-wrapper-bg: $white;
|
||||
$content-wrapper-bg: $white !default;
|
||||
$course-bg-color: #f2f2f2;
|
||||
$course-bg-image: url(../images/bg-texture.png);
|
||||
$course-bg-image: url($static-path + '/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);
|
||||
|
||||
// MISC: course background texture
|
||||
//$course-bg-image: url(../images/bg-texture.png);
|
||||
//$course-bg-image: url($static-path + '/images/bg-texture.png');
|
||||
|
||||
// MISC: borders
|
||||
$border-color-1: rgb(190,190,190);
|
||||
@@ -503,11 +508,11 @@ $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);
|
||||
$login-banner-image: url($static-path + '/images/edx-theme/edx-background-banner-account.png');
|
||||
$register-banner-image: url($static-path + '/images/edx-theme/edx-background-banner-account.png');
|
||||
$passwordreset-banner-image: url($static-path + '/images/edx-theme/edx-background-banner-account.png');
|
||||
|
||||
$video-thumb-url: '../images/homepage-hero-video-thumb.jpg';
|
||||
$video-thumb-url: $static-path + '/images/homepage-hero-video-thumb.jpg';
|
||||
|
||||
// ====================
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
11
lms/static/sass/lms-course-rtl.scss
Normal file
@@ -0,0 +1,11 @@
|
||||
@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';
|
||||
|
||||
//<THEME-OVERRIDE>
|
||||
|
||||
@import 'build-course'; // shared app style assets/rendering
|
||||
@@ -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/<theme-name>/, with its base Sass file in
|
||||
## themes/<theme-name>/static/sass/_<theme-name>.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
|
||||
11
lms/static/sass/lms-course.scss
Normal file
@@ -0,0 +1,11 @@
|
||||
@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';
|
||||
|
||||
//<THEME-OVERRIDE>
|
||||
|
||||
@import 'build-course'; // shared app style assets/rendering
|
||||
@@ -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/<theme-name>/, with its base Sass file in
|
||||
## themes/<theme-name>/static/sass/_<theme-name>.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
|
||||
24
lms/static/sass/lms-footer-rtl.scss
Normal file
@@ -0,0 +1,24 @@
|
||||
// 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';
|
||||
|
||||
//<THEME-OVERRIDE>
|
||||
|
||||
footer#footer-openedx {
|
||||
@import 'base/reset';
|
||||
@import 'base/extends';
|
||||
@import 'base/base';
|
||||
}
|
||||
|
||||
// base - elements
|
||||
@import 'elements/typography';
|
||||
|
||||
// shared - platform
|
||||
@import 'shared/footer';
|
||||
@@ -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/<theme-name>/, with its base Sass file in
|
||||
## themes/<theme-name>/static/sass/_<theme-name>.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';
|
||||
24
lms/static/sass/lms-footer.scss
Normal file
@@ -0,0 +1,24 @@
|
||||
// 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';
|
||||
|
||||
//<THEME-OVERRIDE>
|
||||
|
||||
footer#footer-openedx {
|
||||
@import 'base/reset';
|
||||
@import 'base/extends';
|
||||
@import 'base/base';
|
||||
}
|
||||
|
||||
// base - elements
|
||||
@import 'elements/typography';
|
||||
|
||||
// shared - platform
|
||||
@import 'shared/footer';
|
||||
@@ -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/<theme-name>/, with its base Sass file in
|
||||
## themes/<theme-name>/static/sass/_<theme-name>.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';
|
||||
19
lms/static/sass/lms-main-rtl.scss
Normal file
@@ -0,0 +1,19 @@
|
||||
// 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';
|
||||
|
||||
//<THEME-OVERRIDE>
|
||||
|
||||
@import 'build-lms'; // shared app style assets/rendering
|
||||
@@ -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/<theme-name>/, with its base Sass file in
|
||||
## themes/<theme-name>/static/sass/_<theme-name>.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
|
||||
18
lms/static/sass/lms-main.scss
Normal file
@@ -0,0 +1,18 @@
|
||||
// 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';
|
||||
|
||||
//<THEME-OVERRIDE>
|
||||
|
||||
@import 'build-lms'; // shared app style assets/rendering
|
||||
@@ -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/<theme-name>/, with its base Sass file in
|
||||
## themes/<theme-name>/static/sass/_<theme-name>.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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1311,7 +1311,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 {
|
||||
|
||||
2
lms/static/themed_sass/README.rst
Normal file
@@ -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.
|
||||
19
lms/templates/header.html
Normal file
@@ -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:
|
||||
<link rel="stylesheet" type="text/css" href="${static.url(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
|
||||
@@ -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
|
||||
<link rel="icon" type="image/x-icon" href="${static.url(microsite.get_value('favicon_path', settings.FAVICON_PATH))}" />
|
||||
|
||||
<%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 "",
|
||||
)
|
||||
%>
|
||||
<link rel="stylesheet" href="${static.url(application_css_path)}" type="text/css" media="all" />
|
||||
|
||||
% 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
|
||||
<meta name="path_prefix" content="${EDX_ROOT_URL}">
|
||||
<meta name="google-site-verification" content="_mipQ4AtZQDNmbtOkwehQDOgCxUUV2fb_C0b6wbiRHY" />
|
||||
|
||||
<%include file="${google_analytics_file}" />
|
||||
|
||||
% if style_overrides_file:
|
||||
<link rel="stylesheet" type="text/css" href="${static.url(style_overrides_file)}" />
|
||||
% endif
|
||||
<%static:optional_include_mako file="google_analytics.html" with_microsite=True />
|
||||
|
||||
</head>
|
||||
|
||||
@@ -132,7 +112,7 @@ from branding import api as branding_api
|
||||
<a class="nav-skip" href="<%block name="nav_skip">#content</%block>">${_("Skip to main content")}</a>
|
||||
|
||||
% if not disable_header:
|
||||
<%include file="${header_file}" />
|
||||
<%include file="header.html" />
|
||||
% endif
|
||||
|
||||
<div class="content-wrapper" id="content">
|
||||
@@ -141,16 +121,7 @@ from branding import api as branding_api
|
||||
</div>
|
||||
|
||||
% 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
|
||||
</%block>
|
||||
<%include file="themable-footer.html" />
|
||||
% endif
|
||||
|
||||
% if not disable_window_wrap:
|
||||
|
||||
13
lms/templates/themable-footer.html
Normal file
@@ -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
|
||||
0
openedx/core/djangoapps/theming/__init__.py
Normal file
62
openedx/core/djangoapps/theming/core.py
Normal file
@@ -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)
|
||||
13
openedx/core/djangoapps/theming/startup.py
Normal file
@@ -0,0 +1,13 @@
|
||||
"""
|
||||
Startup code for Comprehensive Theming
|
||||
"""
|
||||
|
||||
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=settings.COMP_THEME_DIR)
|
||||
89
openedx/core/djangoapps/theming/test_util.py
Normal file
@@ -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)
|
||||
0
openedx/core/djangoapps/theming/tests/__init__.py
Normal file
@@ -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", None):
|
||||
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))
|
||||
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
146
themes/README.rst
Normal file
@@ -0,0 +1,146 @@
|
||||
#####################
|
||||
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/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`__.
|
||||
|
||||
.. __: 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.
|
||||
|
||||
|
||||
Installing your theme
|
||||
---------------------
|
||||
|
||||
To use your theme, follow these steps:
|
||||
|
||||
#. Edit /edx/app/edx_ansible/server-vars.yml to add the edxapp_comp_theme_dir
|
||||
value::
|
||||
|
||||
edxapp_comp_theme_dir: '/full/path/to/my-theme'
|
||||
|
||||
#. Re-run the provisioning script::
|
||||
|
||||
$ sudo /edx/bin/update edx-platform HEAD
|
||||
|
||||
Your changes should now be visible on your site.
|
||||
|
||||
|
||||
"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
|
||||
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 993 B After Width: | Height: | Size: 993 B |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 89 KiB |
81
themes/edx.org/lms/templates/footer.html
Normal file
@@ -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.
|
||||
<footer id="footer-edx-v3" role="contentinfo" aria-label="${_("Page Footer")}"
|
||||
## When rendering the footer through the branding API,
|
||||
## the direction may not be set on the parent element,
|
||||
## so we set it here.
|
||||
% if bidi:
|
||||
dir=${bidi}
|
||||
% endif
|
||||
>
|
||||
<h2 class="sr footer-about-title">${_("About edX")}</h2>
|
||||
<div class="footer-content-wrapper">
|
||||
<div class="footer-logo">
|
||||
<img alt="edX logo" src="${static.url("images/logo.png")}">
|
||||
</div>
|
||||
|
||||
<div class="site-details">
|
||||
<nav class="site-nav" aria-label="${_("About edX")}">
|
||||
% for link in footer["navigation_links"]:
|
||||
<a href="${link['url']}">${link['title']}</a>
|
||||
% endfor
|
||||
</nav>
|
||||
<nav class="legal-notices" aria-label="${_("Legal")}">
|
||||
% for link in footer["legal_links"]:
|
||||
<a href="${link['url']}">${link['title']}</a>
|
||||
% endfor
|
||||
</nav>
|
||||
<p class="copyright">${footer['copyright']}</p>
|
||||
|
||||
## The OpenEdX link may be hidden when this view is served
|
||||
## through an API to partner sites (such as marketing sites or blogs),
|
||||
## which are not technically powered by OpenEdX.
|
||||
% if not hide_openedx_link:
|
||||
<div class="openedx-link">
|
||||
<a href="${footer['openedx_link']['url']}" title="${footer['openedx_link']['title']}">
|
||||
<img alt="${footer['openedx_link']['title']}" src="${footer['openedx_link']['image']}" width="140">
|
||||
</a>
|
||||
</div>
|
||||
% endif
|
||||
</div>
|
||||
|
||||
<div class="external-links">
|
||||
<div class="social-media-links">
|
||||
% for link in footer['social_links']:
|
||||
<a href="${link['url']}" class="sm-link external" title="${link['title']}" rel="noreferrer">
|
||||
<span class="icon fa ${link['icon-class']}" aria-hidden="true"></span>
|
||||
</a>
|
||||
% endfor
|
||||
</div>
|
||||
|
||||
<div class="mobile-app-links">
|
||||
% for link in footer['mobile_links']:
|
||||
<a href="${link['url']}" class="app-link external">
|
||||
<img alt="${link['title']}" src="${link['image']}">
|
||||
</a>
|
||||
% endfor
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
% 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:
|
||||
<link rel="stylesheet" type="text/css" href="${url}"></link>
|
||||
% endfor
|
||||
% endif
|
||||
% if footer_js_url:
|
||||
<script type="text/javascript" src="${footer_js_url}"></script>
|
||||
% endif
|
||||
|
||||
157
themes/edx.org/lms/templates/header.html
Normal file
@@ -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:
|
||||
<div class="site-status">
|
||||
<div class="inner-wrapper">
|
||||
<span class="white-error-icon"></span>
|
||||
<p>${site_status_msg}</p>
|
||||
</div>
|
||||
</div>
|
||||
% endif
|
||||
</%block>
|
||||
|
||||
<header class="${"global slim" if course and not disable_courseware_header else "global-new"}" aria-label="Main" role="banner">
|
||||
<div class="${'rwd ' if responsive else ''}wrapper-header nav-container">
|
||||
<h1 class="logo" itemscope="" itemtype="http://schema.org/Organization">
|
||||
<a href="${marketing_link('ROOT')}" title="Home page" itemprop="url">
|
||||
<%block name="navigation_logo">
|
||||
<img src="${static.url("images/logo.png")}" alt="${platform_name()}" title="${platform_name()}" itemprop="url" />
|
||||
<span class="sr">${_("Home Page")}</span>
|
||||
</%block>
|
||||
</a>
|
||||
</h1>
|
||||
|
||||
% if course and not disable_courseware_header:
|
||||
<h2><span class="provider">${course.display_org_with_default | h}:</span> ${course.display_number_with_default | h} ${course.display_name_with_default}</h2>
|
||||
% endif
|
||||
|
||||
% if user.is_authenticated():
|
||||
% if not course or disable_courseware_header:
|
||||
<nav aria-label="Main" class="nav-main">
|
||||
<ul class="left nav-global authenticated">
|
||||
<%block name="navigation_global_links_authenticated">
|
||||
<li class="nav-global-01">
|
||||
<a href="${marketing_link('HOW_IT_WORKS')}">${_("How it Works")}</a>
|
||||
</li>
|
||||
<li class="nav-global-02">
|
||||
<a href="${marketing_link('COURSES')}">${_("Find Courses")}</a>
|
||||
</li>
|
||||
<li class="nav-global-03">
|
||||
<a href="${marketing_link('SCHOOLS')}">${_("Schools & Partners")}</a>
|
||||
</li>
|
||||
</%block>
|
||||
</ul>
|
||||
</nav>
|
||||
% endif
|
||||
|
||||
<ul class="user">
|
||||
<li class="primary">
|
||||
<a href="${reverse('dashboard')}" class="user-link">
|
||||
<i class="icon fa fa-home" aria-hidden="true"></i>
|
||||
<span class="sr">${_("Dashboard for:")}</span>
|
||||
<div>${user.username}</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="primary">
|
||||
<a href="#" class="dropdown" aria-haspopup="true" aria-expanded="false"><span class="sr">${_("More options dropdown")}</span> ▾</a>
|
||||
<ul class="dropdown-menu" aria-label="More Options" role="menu">
|
||||
<%block name="navigation_dropdown_menu_links" >
|
||||
<li><a href="${reverse('account_settings')}">${_("Account Settings")}</a></li>
|
||||
<li><a href="${reverse('learner_profile', kwargs={'username': user.username})}">${_("My Profile")}</a></li>
|
||||
</%block>
|
||||
<li><a href="${reverse('logout')}" role="menuitem">${_("Sign Out")}</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
% if should_display_shopping_cart_func(): # see shoppingcart.context_processor.user_has_cart_context_processor
|
||||
<ul class="user">
|
||||
<li class="primary">
|
||||
<a class="shopping-cart" href="${reverse('shoppingcart.views.show_cart')}">
|
||||
<i class="icon fa fa-shopping-cart" aria-hidden="true"></i> ${_("Shopping Cart")}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
% endif
|
||||
|
||||
% else:
|
||||
<nav aria-label="Main" class="nav-main">
|
||||
<ul class="left nav-global">
|
||||
<%block name="navigation_global_links">
|
||||
<li class="nav-global-01">
|
||||
<a href="${marketing_link('HOW_IT_WORKS')}">${_("How it Works")}</a>
|
||||
</li>
|
||||
<li class="nav-global-02">
|
||||
<a href="${marketing_link('COURSES')}">${_("Find Courses")}</a>
|
||||
</li>
|
||||
<li class="nav-global-03">
|
||||
<a href="${marketing_link('SCHOOLS')}">${_("Schools & Partners")}</a>
|
||||
</li>
|
||||
</%block>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<nav aria-label="Account" class="nav-account-management">
|
||||
<div class="right nav-courseware">
|
||||
% if not settings.FEATURES['DISABLE_LOGIN_BUTTON']:
|
||||
% if course and settings.FEATURES.get('RESTRICT_ENROLL_BY_REG_METHOD') and course.enrollment_domain:
|
||||
<div class="nav-courseware-01">
|
||||
<a class="cta cta-register" href="${reverse('course-specific-register', args=[course.id.to_deprecated_string()])}">${_("Register")}</a>
|
||||
</div>
|
||||
% else:
|
||||
<div class="nav-courseware-01">
|
||||
<a class="cta cta-register" href="/register">${_("Register")}</a>
|
||||
</div>
|
||||
% endif
|
||||
% endif
|
||||
<div class="nav-courseware-02">
|
||||
% if not settings.FEATURES['DISABLE_LOGIN_BUTTON']:
|
||||
% if course and settings.FEATURES.get('RESTRICT_ENROLL_BY_REG_METHOD') and course.enrollment_domain:
|
||||
<a class="cta cta-login nav-courseware-button" href="${reverse('course-specific-login', args=[course.id.to_deprecated_string()])}${login_query()}">${_("Sign in")}</a>
|
||||
% else:
|
||||
<a class="cta cta-login nav-courseware-button" href="/login${login_query()}">${_("Sign in")}</a>
|
||||
% endif
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
% endif
|
||||
</div>
|
||||
</header>
|
||||
% if course:
|
||||
<!--[if lte IE 9]>
|
||||
<div class="ie-banner" aria-hidden="true">${_('<strong>Warning:</strong> Your browser is not fully supported. We strongly recommend using {chrome_link} or {ff_link}.').format(chrome_link='<a href="https://www.google.com/chrome" target="_blank">Chrome</a>', ff_link='<a href="http://www.mozilla.org/firefox" target="_blank">Firefox</a>')}</div>
|
||||
<![endif]-->
|
||||
% endif
|
||||
|
||||
%if not user.is_authenticated():
|
||||
<%include file="forgot_password_modal.html" />
|
||||
%endif
|
||||
|
||||
<%include file="help_modal.html"/>
|
||||
3
themes/red-theme/lms/static/css/README.txt
Normal file
@@ -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.
|
||||
BIN
themes/red-theme/lms/static/images/logo.png
Normal file
|
After Width: | Height: | Size: 909 B |
7
themes/red-theme/lms/static/sass/_overrides.scss
Executable file
@@ -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';
|
||||
5
themes/red-theme/lms/static/sass/lms-main-rtl.scss
Executable file
@@ -0,0 +1,5 @@
|
||||
// Theming overrides for sample theme
|
||||
@import 'overrides';
|
||||
|
||||
// import the rest of the application
|
||||
@import 'lms/static/sass/lms-main-rtl';
|
||||
5
themes/red-theme/lms/static/sass/lms-main.scss
Executable file
@@ -0,0 +1,5 @@
|
||||
// Theming overrides for sample theme
|
||||
@import 'overrides';
|
||||
|
||||
// import the rest of the application
|
||||
@import 'lms/static/sass/lms-main';
|
||||
111
themes/red-theme/lms/templates/footer.html
Executable file
@@ -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
|
||||
%>
|
||||
|
||||
<div class="wrapper wrapper-footer">
|
||||
<footer>
|
||||
<!-- This is super-ugly, don't use it! -->
|
||||
<div class="colophon">
|
||||
<nav class="nav-colophon" aria-label="${_('About')}">
|
||||
<ol>
|
||||
<li class="nav-colophon-01">
|
||||
<a id="about" href="${marketing_link('ABOUT')}">
|
||||
${_("About")}
|
||||
</a>
|
||||
</li>
|
||||
%if marketing_link('JOBS') and marketing_link('JOBS') != '#':
|
||||
<li class="nav-colophon-02">
|
||||
<a id="jobs" href="${marketing_link('JOBS')}">
|
||||
${_("Jobs")}
|
||||
</a>
|
||||
</li>
|
||||
%endif
|
||||
%if marketing_link('NEWS') and marketing_link('NEWS') != '#':
|
||||
<li class="nav-colophon-03">
|
||||
<a id="news" href="${marketing_link('NEWS')}">
|
||||
${_("News")}
|
||||
</a>
|
||||
</li>
|
||||
%endif
|
||||
<li class="nav-colophon-04">
|
||||
<a id="faq" href="${marketing_link('FAQ')}">
|
||||
${_("FAQ")}
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-colophon-05">
|
||||
<a id="contact" href="${marketing_link('CONTACT')}">
|
||||
${_("Contact")}
|
||||
</a>
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<div class="wrapper-logo">
|
||||
<p>
|
||||
<a href="/">
|
||||
## this is just a placeholder logo
|
||||
## feel free to change this logo to your own by replacing "logo.png" with your own logo
|
||||
<img alt="organization logo placeholder" src="${static.url("images/logo.png")}">
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p class="copyright">© ${settings.COPYRIGHT_YEAR} ${settings.PLATFORM_NAME}.</p>
|
||||
|
||||
## Site operators: Please do not remove this paragraph! This attributes back to edX and makes your acknowledgement of edX's trademarks clear.
|
||||
<p class="copyright">
|
||||
## Translators: 'EdX', 'edX', and 'Open edX' are trademarks of 'edX Inc.'. Please do not translate any of these trademarks and company names.
|
||||
${_("EdX, Open edX, and the edX and Open edX logos are registered trademarks or trademarks of {link_start}edX Inc.{link_end}").format(
|
||||
link_start=u"<a href='https://www.edx.org/'>",
|
||||
link_end=u"</a>"
|
||||
)}
|
||||
</p>
|
||||
<nav class="nav-legal" aria-label="${_('Legal')}">
|
||||
<ul>
|
||||
% if marketing_link('HONOR') and marketing_link('HONOR') != '#':
|
||||
<li class="nav-legal-01">
|
||||
<%
|
||||
tos_link = u"<a href='{}'>".format(marketing_link('TOS'))
|
||||
honor_link = u"<a href='{}'>".format(marketing_link('HONOR'))
|
||||
%>
|
||||
${
|
||||
_("{tos_link_start}Terms of Service{tos_link_end} and {honor_link_start}Honor Code{honor_link_end}").format(
|
||||
tos_link_start=tos_link,
|
||||
tos_link_end="</a>",
|
||||
honor_link_start=honor_link,
|
||||
honor_link_end="</a>",
|
||||
)
|
||||
}
|
||||
</li>
|
||||
% else:
|
||||
<li class="nav-legal-01">
|
||||
<a href="${marketing_link('TOS')}">${_("Terms of Service")}</a>
|
||||
</li>
|
||||
% endif
|
||||
<li class="nav-legal-02">
|
||||
<a href="${marketing_link('PRIVACY')}">${_("Privacy Policy")}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
## please leave this link and use one of the logos provided
|
||||
<div class="footer-about-openedx">
|
||||
<p>
|
||||
<a href="http://openedx.org/">
|
||||
## standard powered-by logo
|
||||
## Translators: 'Open edX' is a brand, please keep this untranslated. See http://openedx.org for more information.
|
||||
<img src="https://files.edx.org/openedx-logos/edx-openedx-logo-tag.png" alt="${_('Powered by Open edX')}" width="140" />
|
||||
## greyscale logo for dark background
|
||||
## <img src="https://files.edx.org/openedx-logos/edx-openedx-logo-tag-light.png" alt="${_('Powered by Open edX')}" width="140" />
|
||||
## greyscale logo for light background
|
||||
## <img src="https://files.edx.org/openedx-logos/edx-openedx-logo-tag-dark.png" alt="${_('Powered by Open edX')}" width="140" />
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
164
themes/red-theme/lms/templates/header.html
Executable file
@@ -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:
|
||||
<div class="site-status">
|
||||
<div class="inner-wrapper">
|
||||
<span class="white-error-icon"></span>
|
||||
<p>${site_status_msg}</p>
|
||||
</div>
|
||||
</div>
|
||||
% endif
|
||||
</%block>
|
||||
|
||||
<header id="global-navigation" class="global ${"slim" if course else ""}" >
|
||||
<!-- This file is only for demonstration, and is horrendous! -->
|
||||
<nav aria-label="${_('Global')}">
|
||||
<h1 class="logo">
|
||||
<a href="${marketing_link('ROOT')}">
|
||||
<%block name="navigation_logo">
|
||||
<img src="${static.url("images/logo.png")}" alt="${platform_name()}"/>
|
||||
</%block>
|
||||
</a>
|
||||
</h1>
|
||||
|
||||
% if course:
|
||||
<h2><span class="provider">${course.display_org_with_default | h}:</span>
|
||||
${course.display_number_with_default | h}
|
||||
<%
|
||||
display_name = course.display_name_with_default
|
||||
if settings.FEATURES.get('CUSTOM_COURSES_EDX', False):
|
||||
ccx = get_current_ccx()
|
||||
if ccx:
|
||||
display_name = ccx.display_name
|
||||
%>
|
||||
${display_name}</h2>
|
||||
% endif
|
||||
|
||||
% if user.is_authenticated():
|
||||
<ol class="left nav-global authenticated">
|
||||
<%block name="navigation_global_links_authenticated">
|
||||
% if settings.FEATURES.get('COURSES_ARE_BROWSABLE'):
|
||||
<li class="nav-global-01">
|
||||
<a href="${marketing_link('COURSES')}">${_('Find Courses')}</a>
|
||||
</li>
|
||||
% endif
|
||||
%if settings.FEATURES.get('ENABLE_SYSADMIN_DASHBOARD','') and user.is_staff:
|
||||
<li>
|
||||
## Translators: This is short for "System administration".
|
||||
<a href="${reverse('sysadmin')}">${_("Sysadmin")}</a>
|
||||
</li>
|
||||
%endif
|
||||
</%block>
|
||||
</ol>
|
||||
<ol class="user">
|
||||
<li class="primary">
|
||||
<a href="${reverse('dashboard')}" class="user-link">
|
||||
<i class="icon fa fa-home" aria-hidden="true"></i>
|
||||
<span class="sr">${_("Dashboard for:")}</span>
|
||||
<div>
|
||||
${user.username}
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="primary">
|
||||
<a href="#" class="dropdown" aria-haspopup="true" aria-expanded="false"><span class="sr">${_("More options dropdown")}</span><i class="fa fa-sort-desc" aria-hidden="true"></i></a>
|
||||
<ul class="dropdown-menu" aria-label="More Options" role="menu">
|
||||
<%block name="navigation_dropdown_menu_links" >
|
||||
<li><a href="${reverse('account_settings')}">${_("Account Settings")}</a></li>
|
||||
<li><a href="${reverse('learner_profile', kwargs={'username': user.username})}">${_("My Profile")}</a></li>
|
||||
</%block>
|
||||
<li><a href="${reverse('logout')}" role="menuitem">${_("Sign Out")}</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
% if should_display_shopping_cart_func(): # see shoppingcart.context_processor.user_has_cart_context_processor
|
||||
<ol class="user">
|
||||
<li class="primary">
|
||||
<a class="shopping-cart" href="${reverse('shoppingcart.views.show_cart')}">
|
||||
<i class="icon fa fa-shopping-cart"></i> ${_("Shopping Cart")}
|
||||
</a>
|
||||
</li>
|
||||
</ol>
|
||||
% endif
|
||||
% else:
|
||||
<ol class="left nav-global">
|
||||
<%block name="navigation_global_links">
|
||||
% if microsite.get_value('ENABLE_MKTG_SITE', settings.FEATURES.get('ENABLE_MKTG_SITE', False)):
|
||||
<li class="nav-global-01">
|
||||
<a href="${marketing_link('HOW_IT_WORKS')}">${_("How it Works")}</a>
|
||||
</li>
|
||||
% if settings.FEATURES.get('COURSES_ARE_BROWSABLE'):
|
||||
<li class="nav-global-02">
|
||||
<a href="${marketing_link('COURSES')}">${_("Courses")}</a>
|
||||
</li>
|
||||
% endif
|
||||
<li class="nav-global-03">
|
||||
<a href="${marketing_link('SCHOOLS')}">${_("Schools")}</a>
|
||||
</li>
|
||||
% endif
|
||||
</%block>
|
||||
% if not settings.FEATURES['DISABLE_LOGIN_BUTTON']:
|
||||
% if course and settings.FEATURES.get('RESTRICT_ENROLL_BY_REG_METHOD') and course.enrollment_domain:
|
||||
<li class="nav-global-04">
|
||||
<a class="cta cta-register" href="${reverse('course-specific-register', args=[course.id.to_deprecated_string()])}">${_("Register Now")}</a>
|
||||
</li>
|
||||
% else:
|
||||
<li class="nav-global-04">
|
||||
<a class="cta cta-register" href="/register">${_("Register Now")}</a>
|
||||
</li>
|
||||
% endif
|
||||
% endif
|
||||
</ol>
|
||||
|
||||
<ol class="right nav-courseware">
|
||||
<li class="nav-courseware-01">
|
||||
% if not settings.FEATURES['DISABLE_LOGIN_BUTTON']:
|
||||
% if course and settings.FEATURES.get('RESTRICT_ENROLL_BY_REG_METHOD') and course.enrollment_domain:
|
||||
<a class="cta cta-login" href="${reverse('course-specific-login', args=[course.id.to_deprecated_string()])}${login_query()}">${_("Sign in")}</a>
|
||||
% else:
|
||||
<a class="cta cta-login" href="/login${login_query()}">${_("Sign in")}</a>
|
||||
% endif
|
||||
% endif
|
||||
</li>
|
||||
</ol>
|
||||
% endif
|
||||
</nav>
|
||||
</header>
|
||||
% if course:
|
||||
<!--[if lte IE 8]>
|
||||
<div class="ie-banner" aria-hidden="true">${_('<strong>Warning:</strong> Your browser is not fully supported. We strongly recommend using {chrome_link} or {ff_link}.').format(chrome_link='<a href="https://www.google.com/intl/en/chrome/browser/" target="_blank">Chrome</a>', ff_link='<a href="http://www.mozilla.org/en-US/firefox/new/" target="_blank">Firefox</a>')}</div>
|
||||
<![endif]-->
|
||||
% endif
|
||||
|
||||
%if not user.is_authenticated():
|
||||
<%include file="forgot_password_modal.html" />
|
||||
%endif
|
||||
|
||||
<%include file="help_modal.html"/>
|
||||