Commit b866f356 by John Eskew

Remove support for COMPREHENSIVE_THEME_DIR -

  all dirs must now go into COMPREHENSIVE_THEME_DIRS.
Move comprehensive theming setup section out of startup.py and into
  settings files using new 'derived' functionality.
Add 'derive_settings' at the end of all top-level Django settings files.
Move validation of comprehensive theming settings into new apps.py
  theming file.
Split theming code into code safe to run before settings are initialized
  -and- after settings are initialized.
parent 9a2e25c3
......@@ -15,6 +15,7 @@ import json
from .common import *
from openedx.core.lib.derived import derive_settings
from openedx.core.lib.logsettings import get_logger_config
import os
......@@ -202,10 +203,6 @@ COURSES_WITH_UNSAFE_CODE = ENV_TOKENS.get("COURSES_WITH_UNSAFE_CODE", [])
ASSET_IGNORE_REGEX = ENV_TOKENS.get('ASSET_IGNORE_REGEX', ASSET_IGNORE_REGEX)
# following setting is for backward compatibility
if ENV_TOKENS.get('COMPREHENSIVE_THEME_DIR', None):
COMPREHENSIVE_THEME_DIR = ENV_TOKENS.get('COMPREHENSIVE_THEME_DIR')
COMPREHENSIVE_THEME_DIRS = ENV_TOKENS.get('COMPREHENSIVE_THEME_DIRS', COMPREHENSIVE_THEME_DIRS) or []
# COMPREHENSIVE_THEME_LOCALE_PATHS contain the paths to themes locale directories e.g.
......@@ -534,3 +531,7 @@ PARENTAL_CONSENT_AGE_LIMIT = ENV_TOKENS.get(
# Allow extra middleware classes to be added to the app through configuration.
MIDDLEWARE_CLASSES.extend(ENV_TOKENS.get('EXTRA_MIDDLEWARE_CLASSES', []))
########################## Derive Any Derived Settings #######################
derive_settings(__name__)
......@@ -50,7 +50,7 @@ import lms.envs.common
from lms.envs.common import (
USE_TZ, TECH_SUPPORT_EMAIL, PLATFORM_NAME, PLATFORM_DESCRIPTION, BUGS_EMAIL, DOC_STORE_CONFIG, DATA_DIR,
ALL_LANGUAGES, WIKI_ENABLED, update_module_store_settings, ASSET_IGNORE_REGEX,
PARENTAL_CONSENT_AGE_LIMIT, COMPREHENSIVE_THEME_DIRS, REGISTRATION_EMAIL_PATTERNS_ALLOWED,
PARENTAL_CONSENT_AGE_LIMIT, REGISTRATION_EMAIL_PATTERNS_ALLOWED,
# 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.
......@@ -81,6 +81,8 @@ from lms.envs.common import (
# Enable or disable theming
ENABLE_COMPREHENSIVE_THEMING,
COMPREHENSIVE_THEME_LOCALE_PATHS,
COMPREHENSIVE_THEME_DIRS,
# constants for redirects app
REDIRECT_CACHE_TIMEOUT,
......@@ -113,6 +115,10 @@ from lms.envs.common import (
# Video Image settings
VIDEO_IMAGE_SETTINGS,
VIDEO_TRANSCRIPTS_SETTINGS,
# Methods to derive settings
_make_main_mako_templates,
_make_locale_paths,
)
from path import Path as path
from warnings import simplefilter
......@@ -121,7 +127,12 @@ from lms.djangoapps.lms_xblock.mixin import LmsBlockMixin
from cms.lib.xblock.authoring_mixin import AuthoringMixin
import dealer.git
from xmodule.modulestore.edit_info import EditInfoMixin
from openedx.core.djangoapps.theming.helpers_dirs import (
get_themes_unchecked,
get_theme_base_dirs_from_settings
)
from openedx.core.lib.license import LicenseMixin
from openedx.core.lib.derived import derived, derived_dict_entry
############################ FEATURE CONFIGURATION #############################
......@@ -300,7 +311,7 @@ GEOIPV6_PATH = REPO_ROOT / "common/static/data/geoip/GeoIPv6.dat"
import tempfile
MAKO_MODULE_DIR = os.path.join(tempfile.gettempdir(), 'mako_cms')
MAKO_TEMPLATES = {}
MAKO_TEMPLATES['main'] = [
MAIN_MAKO_TEMPLATES_BASE = [
PROJECT_ROOT / 'templates',
COMMON_ROOT / 'templates',
COMMON_ROOT / 'djangoapps' / 'pipeline_mako' / 'templates',
......@@ -310,9 +321,10 @@ MAKO_TEMPLATES['main'] = [
OPENEDX_ROOT / 'core' / 'lib' / 'license' / 'templates',
CMS_ROOT / 'djangoapps' / 'pipeline_js' / 'templates',
]
MAKO_TEMPLATES['lms.main'] = lms.envs.common.MAIN_MAKO_TEMPLATES_BASE
for namespace, template_dirs in lms.envs.common.MAKO_TEMPLATES.iteritems():
MAKO_TEMPLATES['lms.' + namespace] = template_dirs
MAKO_TEMPLATES['main'] = _make_main_mako_templates
derived_dict_entry('MAKO_TEMPLATES', 'main')
# Django templating
TEMPLATES = [
......@@ -321,7 +333,7 @@ TEMPLATES = [
# Don't look for template source files inside installed applications.
'APP_DIRS': False,
# Instead, look for template source files in these dirs.
'DIRS': MAKO_TEMPLATES['main'],
'DIRS': MAIN_MAKO_TEMPLATES_BASE,
# Options specific to this backend.
'OPTIONS': {
'loaders': (
......@@ -601,8 +613,9 @@ USE_L10N = True
STATICI18N_ROOT = PROJECT_ROOT / "static"
# Localization strings (e.g. django.po) are under this directory
LOCALE_PATHS = (REPO_ROOT + '/conf/locale',) # edx-platform/conf/locale/
# Localization strings (e.g. django.po) are under these directories
LOCALE_PATHS = _make_locale_paths
derived('LOCALE_PATHS')
# Messages
MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'
......@@ -959,7 +972,7 @@ INSTALLED_APPS = [
'webpack_loader',
# Theming
'openedx.core.djangoapps.theming',
'openedx.core.djangoapps.theming.apps.ThemingConfig',
# Site configuration for theming and behavioral modification
'openedx.core.djangoapps.site_configuration',
......@@ -1370,9 +1383,6 @@ AFFILIATE_COOKIE_NAME = 'affiliate_id'
HELP_TOKENS_INI_FILE = REPO_ROOT / "cms" / "envs" / "help_tokens.ini"
# Theme directory locale paths
COMPREHENSIVE_THEME_LOCALE_PATHS = []
# This is required for the migrations in oauth_dispatch.models
# otherwise it fails saying this attribute is not present in Settings
# Although Studio does not exable OAuth2 Provider capability, the new approach
......
......@@ -6,6 +6,7 @@ This config file runs the simplest dev environment"""
# pylint: disable=wildcard-import, unused-wildcard-import
from .common import *
from openedx.core.lib.derived import derive_settings
from openedx.core.lib.logsettings import get_logger_config
# import settings from LMS for consistent behavior with CMS
......@@ -179,3 +180,7 @@ try:
from .private import * # pylint: disable=import-error
except ImportError:
pass
########################## Derive Any Derived Settings #######################
derive_settings(__name__)
......@@ -24,6 +24,7 @@ from path import Path as path
from warnings import filterwarnings, simplefilter
from uuid import uuid4
from util.db import NoOpMigrationModules
from openedx.core.lib.derived import derive_settings
# import settings from LMS for consistent behavior with CMS
# pylint: disable=unused-import
......@@ -360,3 +361,7 @@ VIDEO_TRANSCRIPTS_SETTINGS = dict(
),
DIRECTORY_PREFIX='video-transcripts/',
)
########################## Derive Any Derived Settings #######################
derive_settings(__name__)
......@@ -12,6 +12,7 @@ from the same directory.
# Start with the common settings
from .common import * # pylint: disable=wildcard-import, unused-wildcard-import
from openedx.core.lib.derived import derive_settings
# Use an in-memory database since this settings file is only used for updating assets
DATABASES = {
......@@ -46,3 +47,7 @@ WEBPACK_LOADER['DEFAULT']['STATS_FILE'] = STATIC_ROOT / "webpack-stats.json"
# 1. Uglify is by far the slowest part of the build process
# 2. Having full source code makes debugging tests easier for developers
os.environ['REQUIRE_BUILD_PROFILE_OPTIMIZE'] = 'none'
########################## Derive Any Derived Settings #######################
derive_settings(__name__)
......@@ -17,6 +17,7 @@ defined in the environment:
import yaml
from .common import *
from openedx.core.lib.derived import derive_settings
from openedx.core.lib.logsettings import get_logger_config
from util.config_parse import convert_tokens
import os
......@@ -264,3 +265,7 @@ if FEATURES.get('CUSTOM_COURSES_EDX'):
# Allow extra middleware classes to be added to the app through configuration.
MIDDLEWARE_CLASSES.extend(ENV_TOKENS.get('EXTRA_MIDDLEWARE_CLASSES', []))
########################## Derive Any Derived Settings #######################
derive_settings(__name__)
......@@ -8,17 +8,16 @@ from django.conf import settings
import cms.lib.xblock.runtime
import xmodule.x_module
from openedx.core.djangoapps.monkey_patch import django_db_models_options
from openedx.core.djangoapps.theming.core import enable_theming
from openedx.core.djangoapps.theming.helpers import is_comprehensive_theming_enabled
from openedx.core.lib.django_startup import autostartup
from openedx.core.lib.xblock_utils import xblock_local_resource_url
from openedx.core.release import doc_version
from startup_configurations.validate_config import validate_cms_config
# Force settings to run so that the python path is modified
settings.INSTALLED_APPS # pylint: disable=pointless-statement
from openedx.core.lib.xblock_utils import xblock_local_resource_url
from openedx.core.release import doc_version
from startup_configurations.validate_config import validate_cms_config
def run():
"""
......@@ -29,11 +28,6 @@ def run():
"""
django_db_models_options.patch()
# Comprehensive theming needs to be set up before django startup,
# because modifying django template paths after startup has no effect.
if is_comprehensive_theming_enabled():
enable_theming()
django.setup()
autostartup()
......
......@@ -25,6 +25,7 @@ import warnings
import dateutil
from .common import *
from openedx.core.lib.derived import derive_settings
from openedx.core.lib.logsettings import get_logger_config
import os
......@@ -1068,3 +1069,7 @@ ACE_ROUTING_KEY = ENV_TOKENS.get('ACE_ROUTING_KEY', ACE_ROUTING_KEY)
# Allow extra middleware classes to be added to the app through configuration.
MIDDLEWARE_CLASSES.extend(ENV_TOKENS.get('EXTRA_MIDDLEWARE_CLASSES', []))
########################## Derive Any Derived Settings #######################
derive_settings(__name__)
......@@ -39,9 +39,13 @@ from warnings import simplefilter
from django.utils.translation import ugettext_lazy as _
from .discussionsettings import *
from openedx.core.djangoapps.theming.helpers_dirs import (
get_themes_unchecked,
get_theme_base_dirs_from_settings
)
from openedx.core.lib.derived import derived, derived_dict_entry
from xmodule.modulestore.modulestore_settings import update_module_store_settings
from xmodule.modulestore.edit_info import EditInfoMixin
from openedx.core.lib.license import LicenseMixin
from lms.djangoapps.lms_xblock.mixin import LmsBlockMixin
################################### FEATURES ###################################
......@@ -530,7 +534,7 @@ OAUTH2_PROVIDER_APPLICATION_MODEL = 'oauth2_provider.Application'
import tempfile
MAKO_MODULE_DIR = os.path.join(tempfile.gettempdir(), 'mako_lms')
MAKO_TEMPLATES = {}
MAKO_TEMPLATES['main'] = [
MAIN_MAKO_TEMPLATES_BASE = [
PROJECT_ROOT / 'templates',
COMMON_ROOT / 'templates',
COMMON_ROOT / 'lib' / 'capa' / 'capa' / 'templates',
......@@ -540,6 +544,20 @@ MAKO_TEMPLATES['main'] = [
OPENEDX_ROOT / 'core' / 'lib' / 'license' / 'templates',
]
def _make_main_mako_templates(settings):
"""
Derives the final MAKO_TEMPLATES['main'] setting from other settings.
"""
if settings.ENABLE_COMPREHENSIVE_THEMING:
themes_dirs = get_theme_base_dirs_from_settings(settings.COMPREHENSIVE_THEME_DIRS)
for theme in get_themes_unchecked(themes_dirs, PROJECT_ROOT):
if theme.themes_base_dir not in settings.MAIN_MAKO_TEMPLATES_BASE:
settings.MAIN_MAKO_TEMPLATES_BASE.insert(0, theme.themes_base_dir)
return settings.MAIN_MAKO_TEMPLATES_BASE
MAKO_TEMPLATES['main'] = _make_main_mako_templates
derived_dict_entry('MAKO_TEMPLATES', 'main')
# Django templating
TEMPLATES = [
{
......@@ -1015,8 +1033,18 @@ USE_L10N = True
STATICI18N_ROOT = PROJECT_ROOT / "static"
STATICI18N_OUTPUT_DIR = "js/i18n"
# Localization strings (e.g. django.po) are under this directory
LOCALE_PATHS = (REPO_ROOT + '/conf/locale',) # edx-platform/conf/locale/
# Localization strings (e.g. django.po) are under these directories
def _make_locale_paths(settings):
locale_paths = [settings.REPO_ROOT + '/conf/locale'] # edx-platform/conf/locale/
if settings.ENABLE_COMPREHENSIVE_THEMING:
# Add locale paths to settings for comprehensive theming.
for locale_path in settings.COMPREHENSIVE_THEME_LOCALE_PATHS:
locale_paths += (path(locale_path), )
return locale_paths
LOCALE_PATHS = _make_locale_paths
derived('LOCALE_PATHS')
# Messages
MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'
......@@ -2018,7 +2046,7 @@ INSTALLED_APPS = [
'openedx.core.djangoapps.contentserver',
# Theming
'openedx.core.djangoapps.theming',
'openedx.core.djangoapps.theming.apps.ThemingConfig',
# Site configuration for theming and behavioral modification
'openedx.core.djangoapps.site_configuration',
......
......@@ -13,6 +13,7 @@ sessions. Assumes structure:
# pylint: disable=wildcard-import, unused-wildcard-import
from .common import *
from openedx.core.lib.derived import derive_settings
DEBUG = True
TEMPLATE_DEBUG = True
......@@ -270,3 +271,7 @@ try:
from .private import * # pylint: disable=import-error
except ImportError:
pass
########################## Derive Any Derived Settings #######################
derive_settings(__name__)
......@@ -13,6 +13,7 @@ sessions. Assumes structure:
# pylint: disable=wildcard-import, unused-wildcard-import
from .common import *
from openedx.core.lib.derived import derive_settings
from openedx.core.lib.logsettings import get_logger_config
STATIC_GRAB = True
......@@ -69,3 +70,7 @@ FILE_UPLOAD_HANDLERS = [
'django.core.files.uploadhandler.MemoryFileUploadHandler',
'django.core.files.uploadhandler.TemporaryFileUploadHandler',
]
########################## Derive Any Derived Settings #######################
derive_settings(__name__)
......@@ -25,6 +25,7 @@ from uuid import uuid4
from warnings import filterwarnings, simplefilter
from util.db import NoOpMigrationModules
from openedx.core.lib.derived import derive_settings
from openedx.core.lib.tempdir import mkdtemp_clean
# This patch disables the commit_on_success decorator during tests
......@@ -506,7 +507,7 @@ MICROSITE_LOGISTRATION_HOSTNAME = 'logistration.testserver'
TEST_THEME = COMMON_ROOT / "test" / "test-theme"
# add extra template directory for test-only templates
MAKO_TEMPLATES['main'].extend([
MAIN_MAKO_TEMPLATES_BASE.extend([
COMMON_ROOT / 'test' / 'templates',
COMMON_ROOT / 'test' / 'test_sites',
REPO_ROOT / 'openedx' / 'core' / 'djangolib' / 'tests' / 'templates',
......@@ -605,3 +606,7 @@ ENTERPRISE_CONSENT_API_URL = 'http://enterprise.example.com/consent/api/v1/'
ACTIVATION_EMAIL_FROM_ADDRESS = 'test_activate@edx.org'
TEMPLATES[0]['OPTIONS']['debug'] = True
########################## Derive Any Derived Settings #######################
derive_settings(__name__)
......@@ -12,6 +12,7 @@ from the same directory.
# Start with the common settings
from .common import * # pylint: disable=wildcard-import, unused-wildcard-import
from openedx.core.lib.derived import derive_settings
# Use an in-memory database since this settings file is only used for updating assets
DATABASES = {
......@@ -59,3 +60,7 @@ WEBPACK_LOADER['DEFAULT']['STATS_FILE'] = STATIC_ROOT / "webpack-stats.json"
# 1. Uglify is by far the slowest part of the build process
# 2. Having full source code makes debugging tests easier for developers
os.environ['REQUIRE_BUILD_PROFILE_OPTIMIZE'] = 'none'
########################## Derive Any Derived Settings #######################
derive_settings(__name__)
......@@ -16,6 +16,7 @@ defined in the environment:
import yaml
from .common import *
from openedx.core.lib.derived import derive_settings
from openedx.core.lib.logsettings import get_logger_config
from util.config_parse import convert_tokens
import os
......@@ -329,3 +330,7 @@ CREDENTIALS_GENERATION_ROUTING_KEY = HIGH_PRIORITY_QUEUE
# Allow extra middleware classes to be added to the app through configuration.
MIDDLEWARE_CLASSES.extend(ENV_TOKENS.get('EXTRA_MIDDLEWARE_CLASSES', []))
########################## Derive Any Derived Settings #######################
derive_settings(__name__)
......@@ -21,8 +21,6 @@ import xmodule.x_module
import lms_xblock.runtime
from startup_configurations.validate_config import validate_lms_config
from openedx.core.djangoapps.theming.core import enable_theming
from openedx.core.djangoapps.theming.helpers import is_comprehensive_theming_enabled
from microsite_configuration import microsite
......@@ -38,11 +36,6 @@ def run():
"""
django_db_models_options.patch()
# Comprehensive theming needs to be set up before django startup,
# because modifying django template paths after startup has no effect.
if is_comprehensive_theming_enabled():
enable_theming()
# We currently use 2 template rendering engines, mako and django_templates,
# and one of them (django templates), requires the directories be added
# before the django.setup().
......
import os
import six
from django.apps import AppConfig
from django.conf import settings
from django.core.checks import Error, Tags, register
class ThemingConfig(AppConfig):
name = 'openedx.core.djangoapps.theming'
verbose_name = "Theming"
@register(Tags.compatibility)
def check_comprehensive_theme_settings(app_configs, **kwargs):
"""
Checks the comprehensive theming theme directory settings.
Raises compatibility Errors upon:
- COMPREHENSIVE_THEME_DIRS is not a list
- theme dir path is not a string
- theme dir path is not an absolute path
- path specified in COMPREHENSIVE_THEME_DIRS does not exist
Returns:
List of any Errors.
"""
if not getattr(settings, "ENABLE_COMPREHENSIVE_THEMING"):
# Only perform checks when comprehensive theming is enabled.
return []
errors = []
# COMPREHENSIVE_THEME_DIR is no longer supported - support has been removed.
if hasattr(settings, "COMPREHENSIVE_THEME_DIR"):
theme_dir = settings.COMPREHENSIVE_THEME_DIR
errors.append(
Error(
"COMPREHENSIVE_THEME_DIR setting has been removed in favor of COMPREHENSIVE_THEME_DIRS.",
hint='Transfer the COMPREHENSIVE_THEME_DIR value to COMPREHENSIVE_THEME_DIRS.',
obj=theme_dir,
id='openedx.core.djangoapps.theming.E001',
)
)
if hasattr(settings, "COMPREHENSIVE_THEME_DIRS"):
theme_dirs = settings.COMPREHENSIVE_THEME_DIRS
if not isinstance(theme_dirs, list):
errors.append(
Error(
"COMPREHENSIVE_THEME_DIRS must be a list.",
obj=theme_dirs,
id='openedx.core.djangoapps.theming.E004',
)
)
if not all([isinstance(theme_dir, six.string_types) for theme_dir in theme_dirs]):
errors.append(
Error(
"COMPREHENSIVE_THEME_DIRS must contain only strings.",
obj=theme_dirs,
id='openedx.core.djangoapps.theming.E005',
)
)
if not all([theme_dir.startswith("/") for theme_dir in theme_dirs]):
errors.append(
Error(
"COMPREHENSIVE_THEME_DIRS must contain only absolute paths to themes dirs.",
obj=theme_dirs,
id='openedx.core.djangoapps.theming.E006',
)
)
if not all([os.path.isdir(theme_dir) for theme_dir in theme_dirs]):
errors.append(
Error(
"COMPREHENSIVE_THEME_DIRS must contain valid paths.",
obj=theme_dirs,
id='openedx.core.djangoapps.theming.E007',
)
)
return errors
"""
Core logic for Comprehensive Theming.
"""
from logging import getLogger
from django.conf import settings
from path import Path as path
from .helpers import get_themes
logger = getLogger(__name__) # pylint: disable=invalid-name
def enable_theming():
"""
Add directories and relevant paths to settings for comprehensive theming.
"""
# Deprecated Warnings
if hasattr(settings, "COMPREHENSIVE_THEME_DIR"):
logger.warning(
"\033[93m \nDeprecated: "
"\n\tCOMPREHENSIVE_THEME_DIR setting has been deprecated in favor of COMPREHENSIVE_THEME_DIRS.\033[00m"
)
for theme in get_themes():
if theme.themes_base_dir not in settings.MAKO_TEMPLATES['main']:
settings.MAKO_TEMPLATES['main'].insert(0, theme.themes_base_dir)
_add_theming_locales()
def _add_theming_locales():
"""
Add locale paths to settings for comprehensive theming.
"""
theme_locale_paths = settings.COMPREHENSIVE_THEME_LOCALE_PATHS
for locale_path in theme_locale_paths:
settings.LOCALE_PATHS += (path(locale_path), ) # pylint: disable=no-member
"""
Code which dynamically discovers comprehensive themes. Deliberately uses no Django settings,
as the discovery happens during the initial setup of Django settings.
"""
import os
from path import Path
def get_theme_base_dirs_from_settings(theme_dirs=None):
"""
Return base directories that contains all the themes.
Example:
>> get_theme_base_dirs_from_settings('/edx/app/ecommerce/ecommerce/themes')
['/edx/app/ecommerce/ecommerce/themes']
Returns:
(List of Paths): Base theme directory paths
"""
theme_base_dirs = []
if theme_dirs:
theme_base_dirs.extend([Path(theme_dir) for theme_dir in theme_dirs])
return theme_base_dirs
def get_themes_unchecked(themes_dirs, project_root=None):
"""
Returns a list of all themes known to the system.
Args:
themes_dirs (list): Paths to themes base directory
project_root (str): (optional) Path to project root
Returns:
List of themes known to the system.
"""
themes_base_dirs = [Path(themes_dir) for themes_dir in themes_dirs]
# pick only directories and discard files in themes directory
themes = []
for themes_dir in themes_base_dirs:
themes.extend([Theme(name, name, themes_dir, project_root) for name in get_theme_dirs(themes_dir)])
return themes
def get_theme_dirs(themes_dir=None):
"""
Returns theme dirs in given dirs
Args:
themes_dir (Path): base dir that contains themes.
"""
return [_dir for _dir in os.listdir(themes_dir) if is_theme_dir(themes_dir / _dir)]
def is_theme_dir(_dir):
"""
Returns true if given dir contains theme overrides.
A theme dir must have subdirectory 'lms' or 'cms' or both.
Args:
_dir: directory path to check for a theme
Returns:
Returns true if given dir is a theme directory.
"""
theme_sub_directories = {'lms', 'cms'}
return bool(os.path.isdir(_dir) and theme_sub_directories.intersection(os.listdir(_dir)))
def get_project_root_name_from_settings(project_root):
"""
Return root name for the current project
Example:
>> get_project_root_name()
'lms'
# from studio
>> get_project_root_name()
'cms'
Args:
project_root (str): Root directory of the project.
Returns:
(str): component name of platform e.g lms, cms
"""
root = Path(project_root)
if root.name == "":
root = root.parent
return root.name
class Theme(object):
"""
class to encapsulate theme related information.
"""
name = ''
theme_dir_name = ''
themes_base_dir = None
project_root = None
def __init__(self, name='', theme_dir_name='', themes_base_dir=None, project_root=None):
"""
init method for Theme
Args:
name: name if the theme
theme_dir_name: directory name of the theme
themes_base_dir: directory path of the folder that contains the theme
"""
self.name = name
self.theme_dir_name = theme_dir_name
self.themes_base_dir = themes_base_dir
self.project_root = project_root
def __eq__(self, other):
"""
Returns True if given theme is same as the self
Args:
other: Theme object to compare with self
Returns:
(bool) True if two themes are the same else False
"""
return (self.theme_dir_name, self.path) == (other.theme_dir_name, other.path)
def __hash__(self):
return hash((self.theme_dir_name, self.path))
def __unicode__(self):
return u"<Theme: {name} at '{path}'>".format(name=self.name, path=self.path)
def __repr__(self):
return self.__unicode__()
@property
def path(self):
"""
Get absolute path of the directory that contains current theme's templates, static assets etc.
Returns:
Path: absolute path to current theme's contents
"""
return Path(self.themes_base_dir) / self.theme_dir_name / get_project_root_name_from_settings(self.project_root)
@property
def template_path(self):
"""
Get absolute path of current theme's template directory.
Returns:
Path: absolute path to current theme's template directory
"""
return Path(self.theme_dir_name) / get_project_root_name_from_settings(self.project_root) / 'templates'
@property
def template_dirs(self):
"""
Get a list of all template directories for current theme.
Returns:
list: list of all template directories for current theme.
"""
return [
self.path / 'templates',
]
from django.contrib.staticfiles.storage import staticfiles_storage
def get_static_file_url(asset):
"""
Returns url of the themed asset if asset is not themed than returns the default asset url.
Example:
>> get_static_file_url('css/lms-main-v1.css')
'/static/red-theme/css/lms-main-v1.css'
Parameters:
asset (str): asset's path relative to the static files directory
Returns:
(str): static asset's url
"""
return staticfiles_storage.url(asset)
......@@ -92,7 +92,7 @@ class Command(BaseCommand):
if theme_dirs:
available_themes = {}
for theme_dir in theme_dirs:
available_themes.update({t.theme_dir_name: t for t in get_themes(theme_dir)})
available_themes.update({t.theme_dir_name: t for t in get_themes([theme_dir])})
else:
theme_dirs = get_theme_base_dirs()
available_themes = {t.theme_dir_name: t for t in get_themes()}
......
......@@ -9,7 +9,7 @@ from django.utils.safestring import mark_safe
from pipeline.templatetags.pipeline import StylesheetNode, JavascriptNode
from pipeline.utils import guess_type
from openedx.core.djangoapps.theming.helpers import get_static_file_url
from openedx.core.djangoapps.theming.helpers_static import get_static_file_url
register = template.Library() # pylint: disable=invalid-name
......
......@@ -22,13 +22,13 @@ class TestHelpers(TestCase):
Tests template paths are returned from enabled theme.
"""
expected_themes = [
Theme('dark-theme', 'dark-theme', get_theme_base_dir('dark-theme')),
Theme('edge.edx.org', 'edge.edx.org', get_theme_base_dir('edge.edx.org')),
Theme('edx.org', 'edx.org', get_theme_base_dir('edx.org')),
Theme('open-edx', 'open-edx', get_theme_base_dir('open-edx')),
Theme('red-theme', 'red-theme', get_theme_base_dir('red-theme')),
Theme('stanford-style', 'stanford-style', get_theme_base_dir('stanford-style')),
Theme('test-theme', 'test-theme', get_theme_base_dir('test-theme')),
Theme('dark-theme', 'dark-theme', get_theme_base_dir('dark-theme'), settings.PROJECT_ROOT),
Theme('edge.edx.org', 'edge.edx.org', get_theme_base_dir('edge.edx.org'), settings.PROJECT_ROOT),
Theme('edx.org', 'edx.org', get_theme_base_dir('edx.org'), settings.PROJECT_ROOT),
Theme('open-edx', 'open-edx', get_theme_base_dir('open-edx'), settings.PROJECT_ROOT),
Theme('red-theme', 'red-theme', get_theme_base_dir('red-theme'), settings.PROJECT_ROOT),
Theme('stanford-style', 'stanford-style', get_theme_base_dir('stanford-style'), settings.PROJECT_ROOT),
Theme('test-theme', 'test-theme', get_theme_base_dir('test-theme'), settings.PROJECT_ROOT),
]
actual_themes = get_themes()
self.assertItemsEqual(expected_themes, actual_themes)
......@@ -39,7 +39,7 @@ class TestHelpers(TestCase):
Tests template paths are returned from enabled theme.
"""
expected_themes = [
Theme('test-theme', 'test-theme', get_theme_base_dir('test-theme')),
Theme('test-theme', 'test-theme', get_theme_base_dir('test-theme'), settings.PROJECT_ROOT),
]
actual_themes = get_themes()
self.assertItemsEqual(expected_themes, actual_themes)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment