2012-02-04 23:40:31 +00:00
|
|
|
# Copyright 2012 United States Government as represented by the
|
2011-07-04 04:10:53 +00:00
|
|
|
# Administrator of the National Aeronautics and Space Administration.
|
|
|
|
# All Rights Reserved.
|
|
|
|
#
|
2012-02-04 23:40:31 +00:00
|
|
|
# Copyright 2012 Nebula, Inc.
|
2011-07-04 04:10:53 +00:00
|
|
|
#
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
|
|
# not use this file except in compliance with the License. You may obtain
|
|
|
|
# a copy of the License at
|
|
|
|
#
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
#
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
|
# License for the specific language governing permissions and limitations
|
|
|
|
# under the License.
|
|
|
|
|
2016-11-30 11:15:36 +00:00
|
|
|
import glob
|
2011-01-12 21:43:31 +00:00
|
|
|
import logging
|
|
|
|
import os
|
2018-09-08 17:22:03 +00:00
|
|
|
import sys
|
2020-02-19 08:17:47 +00:00
|
|
|
import warnings
|
2011-01-12 21:43:31 +00:00
|
|
|
|
2014-01-03 16:31:49 +00:00
|
|
|
from django.utils.translation import ugettext_lazy as _
|
2013-09-11 10:09:42 +00:00
|
|
|
|
2018-04-11 09:57:28 +00:00
|
|
|
from horizon.utils.escape import monkeypatch_escape
|
|
|
|
|
|
|
|
from openstack_dashboard import enabled
|
2015-04-06 21:45:55 +00:00
|
|
|
from openstack_dashboard import exceptions
|
2018-04-11 09:57:28 +00:00
|
|
|
from openstack_dashboard.local import enabled as local_enabled
|
Dynamic Themes
Horizon themes are now configurable at a user level, through the use
of cookies. The themes that can be set are configurable at a
deployment level through settings.py. Horizon can be configured to
run with multiple themes, and allow users to choose which themes
they wish to run.
Django Compressor:
In order to support dynamic themes, each theme configuration must
be pre-compiled through the Django compressor. By making use of its
built in COMPRESS_OFFLINE_CONTEXT, we now return a generator to
create each of the theme's necessary offline contexts.
Templates:
Horizon themes allowed template overrides via their 'templates'
subfolder. In order to maintain this parity, a custom theme template
loader was created. It is run before the other loads, and simply
looks for a Django template in the current theme (cookie driven)
before diverting to the previous template loaders.
Static Files:
Horizon themes allowed static overrides of the images in
'dashboard/img' folder. A template tag, 'themable_asset' was created
to maintain this parity. Any asset that is wished to be made themable,
given that it is located in Horizon's 'static/dashboard' folder, can
now be made ot be themable. By making this a template tag, this
gives the developers more granular control over what branders can
customize.
Angular and Plugins:
By far, the trickiest part of this task, Angular and Plugins are
dynamic in the files that they 'discover'. SCSS is not flexible in
this manner at ALL. SCSS disallows the importation of a variable
name. To get around this, themes.scss was created as a Django
template. This template is the top level import file for all styles
within Horizon, and therefore, allows ALL the scss files to share a
common namespace and thus, can use shared variables as well as extend
shared styles.
Other:
This change is fundamental, in that it changes the method by which
Horizon ingests its SCSS files. Many problems existing in the
previous implementation, in an effort to make Horizon flexible, its
SCSS was made very inflexible. This patch corrects those problems.
Change-Id: Ic48b4b5c1d1a41f1e01a8d52784c9d38d192c8f1
Implements: blueprint horizon-dynamic-theme
Closes-Bug: #1480427
2016-01-30 16:42:15 +00:00
|
|
|
from openstack_dashboard import theme_settings
|
2016-11-30 11:15:36 +00:00
|
|
|
from openstack_dashboard.utils import config
|
2015-07-22 17:15:45 +00:00
|
|
|
from openstack_dashboard.utils import settings as settings_utils
|
2015-01-13 13:45:07 +00:00
|
|
|
|
2016-05-03 05:51:49 +00:00
|
|
|
monkeypatch_escape()
|
2012-08-05 19:52:21 +00:00
|
|
|
|
2019-04-18 00:12:57 +00:00
|
|
|
# Load default values
|
|
|
|
# pylint: disable=wrong-import-position
|
2020-05-10 22:29:47 +00:00
|
|
|
from openstack_dashboard.defaults import * # noqa: E402,F403,H303
|
2019-04-18 00:12:57 +00:00
|
|
|
|
2017-06-01 02:36:38 +00:00
|
|
|
_LOG = logging.getLogger(__name__)
|
|
|
|
|
2020-02-19 08:17:47 +00:00
|
|
|
warnings.filterwarnings("default", category=DeprecationWarning)
|
|
|
|
|
2011-01-12 21:43:31 +00:00
|
|
|
ROOT_PATH = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
|
2018-09-08 17:22:03 +00:00
|
|
|
if ROOT_PATH not in sys.path:
|
|
|
|
sys.path.append(ROOT_PATH)
|
2011-01-12 21:43:31 +00:00
|
|
|
|
|
|
|
DEBUG = False
|
|
|
|
|
2012-02-29 07:27:46 +00:00
|
|
|
ROOT_URLCONF = 'openstack_dashboard.urls'
|
|
|
|
|
|
|
|
HORIZON_CONFIG = {
|
2012-10-04 22:43:40 +00:00
|
|
|
'user_home': 'openstack_dashboard.views.get_user_home',
|
2012-08-02 23:39:52 +00:00
|
|
|
'ajax_queue_limit': 10,
|
2013-03-07 00:21:12 +00:00
|
|
|
'auto_fade_alerts': {
|
|
|
|
'delay': 3000,
|
|
|
|
'fade_duration': 1500,
|
|
|
|
'types': ['alert-success', 'alert-info']
|
|
|
|
},
|
2015-09-24 18:34:52 +00:00
|
|
|
'bug_url': None,
|
2018-03-20 07:19:57 +00:00
|
|
|
'help_url': "https://docs.openstack.org/",
|
2015-04-06 21:45:55 +00:00
|
|
|
'exceptions': {'recoverable': exceptions.RECOVERABLE,
|
|
|
|
'not_found': exceptions.NOT_FOUND,
|
|
|
|
'unauthorized': exceptions.UNAUTHORIZED},
|
2015-08-12 16:12:18 +00:00
|
|
|
'modal_backdrop': 'static',
|
2014-05-08 18:34:21 +00:00
|
|
|
'angular_modules': [],
|
|
|
|
'js_files': [],
|
2015-02-09 06:00:18 +00:00
|
|
|
'js_spec_files': [],
|
2015-06-20 01:21:30 +00:00
|
|
|
'external_templates': [],
|
2016-03-03 13:27:10 +00:00
|
|
|
'plugins': [],
|
|
|
|
'integration_tests_support': INTEGRATION_TESTS_SUPPORT
|
2012-02-29 07:27:46 +00:00
|
|
|
}
|
2011-01-12 21:43:31 +00:00
|
|
|
|
2017-12-16 12:52:24 +00:00
|
|
|
MIDDLEWARE = (
|
2018-11-01 02:24:31 +00:00
|
|
|
'openstack_auth.middleware.OpenstackAuthMonkeyPatchMiddleware',
|
2018-08-24 00:37:46 +00:00
|
|
|
'debreach.middleware.RandomCommentMiddleware',
|
2011-01-12 21:43:31 +00:00
|
|
|
'django.middleware.common.CommonMiddleware',
|
|
|
|
'django.middleware.csrf.CsrfViewMiddleware',
|
|
|
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
2012-04-14 04:46:04 +00:00
|
|
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
2016-01-18 04:04:35 +00:00
|
|
|
'horizon.middleware.OperationLogMiddleware',
|
2011-01-12 21:43:31 +00:00
|
|
|
'django.contrib.messages.middleware.MessageMiddleware',
|
2011-10-31 18:31:05 +00:00
|
|
|
'horizon.middleware.HorizonMiddleware',
|
Dynamic Themes
Horizon themes are now configurable at a user level, through the use
of cookies. The themes that can be set are configurable at a
deployment level through settings.py. Horizon can be configured to
run with multiple themes, and allow users to choose which themes
they wish to run.
Django Compressor:
In order to support dynamic themes, each theme configuration must
be pre-compiled through the Django compressor. By making use of its
built in COMPRESS_OFFLINE_CONTEXT, we now return a generator to
create each of the theme's necessary offline contexts.
Templates:
Horizon themes allowed template overrides via their 'templates'
subfolder. In order to maintain this parity, a custom theme template
loader was created. It is run before the other loads, and simply
looks for a Django template in the current theme (cookie driven)
before diverting to the previous template loaders.
Static Files:
Horizon themes allowed static overrides of the images in
'dashboard/img' folder. A template tag, 'themable_asset' was created
to maintain this parity. Any asset that is wished to be made themable,
given that it is located in Horizon's 'static/dashboard' folder, can
now be made ot be themable. By making this a template tag, this
gives the developers more granular control over what branders can
customize.
Angular and Plugins:
By far, the trickiest part of this task, Angular and Plugins are
dynamic in the files that they 'discover'. SCSS is not flexible in
this manner at ALL. SCSS disallows the importation of a variable
name. To get around this, themes.scss was created as a Django
template. This template is the top level import file for all styles
within Horizon, and therefore, allows ALL the scss files to share a
common namespace and thus, can use shared variables as well as extend
shared styles.
Other:
This change is fundamental, in that it changes the method by which
Horizon ingests its SCSS files. Many problems existing in the
previous implementation, in an effort to make Horizon flexible, its
SCSS was made very inflexible. This patch corrects those problems.
Change-Id: Ic48b4b5c1d1a41f1e01a8d52784c9d38d192c8f1
Implements: blueprint horizon-dynamic-theme
Closes-Bug: #1480427
2016-01-30 16:42:15 +00:00
|
|
|
'horizon.themes.ThemeMiddleware',
|
2011-06-03 05:23:25 +00:00
|
|
|
'django.middleware.locale.LocaleMiddleware',
|
2013-02-13 09:43:11 +00:00
|
|
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
2016-04-08 15:40:44 +00:00
|
|
|
'openstack_dashboard.contrib.developer.profiler.middleware.'
|
|
|
|
'ProfilerClientMiddleware',
|
|
|
|
'openstack_dashboard.contrib.developer.profiler.middleware.'
|
|
|
|
'ProfilerMiddleware',
|
2011-01-12 21:43:31 +00:00
|
|
|
)
|
|
|
|
|
2016-08-04 19:41:46 +00:00
|
|
|
CACHED_TEMPLATE_LOADERS = [
|
2016-04-11 11:40:44 +00:00
|
|
|
'django.template.loaders.filesystem.Loader',
|
|
|
|
'django.template.loaders.app_directories.Loader',
|
2016-08-04 19:41:46 +00:00
|
|
|
'horizon.loaders.TemplateLoader'
|
|
|
|
]
|
2016-04-11 11:40:44 +00:00
|
|
|
|
|
|
|
ADD_TEMPLATE_LOADERS = []
|
2018-12-22 20:57:41 +00:00
|
|
|
ADD_TEMPLATE_DIRS = []
|
2016-04-11 11:40:44 +00:00
|
|
|
|
2016-08-04 19:41:46 +00:00
|
|
|
TEMPLATES = [
|
|
|
|
{
|
|
|
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
|
|
|
'DIRS': [os.path.join(ROOT_PATH, 'templates')],
|
|
|
|
'OPTIONS': {
|
|
|
|
'context_processors': [
|
|
|
|
'django.template.context_processors.debug',
|
|
|
|
'django.template.context_processors.i18n',
|
|
|
|
'django.template.context_processors.request',
|
|
|
|
'django.template.context_processors.media',
|
|
|
|
'django.template.context_processors.static',
|
|
|
|
'django.contrib.messages.context_processors.messages',
|
|
|
|
'horizon.context_processors.horizon',
|
|
|
|
'openstack_dashboard.context_processors.openstack',
|
|
|
|
],
|
|
|
|
'loaders': [
|
|
|
|
'horizon.themes.ThemeTemplateLoader'
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
]
|
2011-01-12 21:43:31 +00:00
|
|
|
|
2012-05-12 06:37:35 +00:00
|
|
|
STATICFILES_FINDERS = (
|
2014-03-24 14:40:03 +00:00
|
|
|
'django.contrib.staticfiles.finders.FileSystemFinder',
|
2016-02-10 21:48:31 +00:00
|
|
|
'horizon.contrib.staticfiles.finders.HorizonStaticFinder',
|
2014-08-25 08:58:39 +00:00
|
|
|
'compressor.finders.CompressorFinder',
|
2011-08-30 00:41:35 +00:00
|
|
|
)
|
2014-03-24 14:40:03 +00:00
|
|
|
|
2012-05-12 06:37:35 +00:00
|
|
|
COMPRESS_PRECOMPILERS = (
|
2015-08-15 03:51:21 +00:00
|
|
|
('text/scss', 'horizon.utils.scss_filter.HorizonScssFilter'),
|
2012-05-12 06:37:35 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
COMPRESS_CSS_FILTERS = (
|
|
|
|
'compressor.filters.css_default.CssAbsoluteFilter',
|
|
|
|
)
|
|
|
|
|
|
|
|
COMPRESS_ENABLED = True
|
|
|
|
COMPRESS_OUTPUT_DIR = 'dashboard'
|
|
|
|
COMPRESS_CSS_HASHING_METHOD = 'hash'
|
2012-06-11 21:20:32 +00:00
|
|
|
COMPRESS_PARSER = 'compressor.parser.HtmlParser'
|
2012-05-12 06:37:35 +00:00
|
|
|
|
2013-11-14 11:43:05 +00:00
|
|
|
INSTALLED_APPS = [
|
2012-02-29 07:27:46 +00:00
|
|
|
'openstack_dashboard',
|
2012-04-14 04:46:04 +00:00
|
|
|
'django.contrib.contenttypes',
|
|
|
|
'django.contrib.auth',
|
2011-01-12 21:43:31 +00:00
|
|
|
'django.contrib.sessions',
|
|
|
|
'django.contrib.messages',
|
2011-08-30 00:41:35 +00:00
|
|
|
'django.contrib.staticfiles',
|
2012-06-23 20:58:35 +00:00
|
|
|
'django.contrib.humanize',
|
2014-04-25 13:37:48 +00:00
|
|
|
'django_pyscss',
|
2018-08-24 00:37:46 +00:00
|
|
|
'debreach',
|
2014-09-22 15:50:12 +00:00
|
|
|
'openstack_dashboard.django_pyscss_fix',
|
2012-05-12 06:37:35 +00:00
|
|
|
'compressor',
|
2011-10-31 18:31:05 +00:00
|
|
|
'horizon',
|
2012-04-14 04:46:04 +00:00
|
|
|
'openstack_auth',
|
2013-11-14 11:43:05 +00:00
|
|
|
]
|
2011-01-12 21:43:31 +00:00
|
|
|
|
2012-04-14 04:46:04 +00:00
|
|
|
AUTHENTICATION_BACKENDS = ('openstack_auth.backend.KeystoneBackend',)
|
2015-09-11 06:16:44 +00:00
|
|
|
AUTH_USER_MODEL = 'openstack_auth.User'
|
2014-06-23 02:35:20 +00:00
|
|
|
MESSAGE_STORAGE = 'django.contrib.messages.storage.fallback.FallbackStorage'
|
2011-01-12 21:43:31 +00:00
|
|
|
|
2019-01-14 22:42:06 +00:00
|
|
|
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
|
|
|
|
CACHES = {
|
|
|
|
'default': {
|
|
|
|
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
|
|
|
|
'LOCATION': '127.0.0.1:11211',
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2012-04-11 21:04:08 +00:00
|
|
|
SESSION_COOKIE_HTTPONLY = True
|
2011-01-12 21:43:31 +00:00
|
|
|
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
|
2012-04-11 21:04:08 +00:00
|
|
|
SESSION_COOKIE_SECURE = False
|
2015-05-01 21:31:07 +00:00
|
|
|
|
2013-11-26 08:47:39 +00:00
|
|
|
# when doing upgrades, it may be wise to stick to PickleSerializer
|
2014-07-24 10:14:54 +00:00
|
|
|
# NOTE(berendt): Check during the K-cycle if this variable can be removed.
|
|
|
|
# https://bugs.launchpad.net/horizon/+bug/1349463
|
2013-11-26 08:47:39 +00:00
|
|
|
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
|
|
|
|
|
2018-01-17 01:11:59 +00:00
|
|
|
CSRF_FAILURE_VIEW = 'openstack_dashboard.views.csrf_failure'
|
|
|
|
|
2017-08-12 22:30:13 +00:00
|
|
|
LANGUAGES = (
|
|
|
|
('cs', 'Czech'),
|
|
|
|
('de', 'German'),
|
|
|
|
('en', 'English'),
|
|
|
|
('en-au', 'Australian English'),
|
|
|
|
('en-gb', 'British English'),
|
2018-02-21 03:36:20 +00:00
|
|
|
('eo', 'Esperanto'),
|
2017-08-12 22:30:13 +00:00
|
|
|
('es', 'Spanish'),
|
|
|
|
('fr', 'French'),
|
|
|
|
('id', 'Indonesian'),
|
|
|
|
('it', 'Italian'),
|
|
|
|
('ja', 'Japanese'),
|
|
|
|
('ko', 'Korean (Korea)'),
|
|
|
|
('pl', 'Polish'),
|
|
|
|
('pt-br', 'Portuguese (Brazil)'),
|
|
|
|
('ru', 'Russian'),
|
|
|
|
('tr', 'Turkish'),
|
|
|
|
('zh-cn', 'Simplified Chinese'),
|
|
|
|
('zh-tw', 'Chinese (Taiwan)'),
|
|
|
|
)
|
2011-06-03 05:23:25 +00:00
|
|
|
LANGUAGE_CODE = 'en'
|
2013-09-14 00:25:02 +00:00
|
|
|
LANGUAGE_COOKIE_NAME = 'horizon_language'
|
2011-06-02 06:23:09 +00:00
|
|
|
USE_I18N = True
|
2012-06-22 02:33:42 +00:00
|
|
|
USE_L10N = True
|
|
|
|
USE_TZ = True
|
2011-01-12 21:43:31 +00:00
|
|
|
|
2012-05-24 22:25:35 +00:00
|
|
|
DEFAULT_EXCEPTION_REPORTER_FILTER = 'horizon.exceptions.HorizonReporterFilter'
|
|
|
|
|
2013-09-24 18:26:59 +00:00
|
|
|
SECRET_KEY = None
|
2014-01-09 22:35:38 +00:00
|
|
|
LOCAL_PATH = None
|
2013-09-24 18:26:59 +00:00
|
|
|
|
2014-07-17 15:19:52 +00:00
|
|
|
ADD_INSTALLED_APPS = []
|
2015-02-27 23:50:27 +00:00
|
|
|
|
2015-12-17 06:16:35 +00:00
|
|
|
CSRF_COOKIE_AGE = None
|
|
|
|
|
|
|
|
COMPRESS_OFFLINE_CONTEXT = 'horizon.themes.offline_context'
|
|
|
|
|
|
|
|
# Notice all customizable configurations should be above this line
|
2015-07-22 17:15:45 +00:00
|
|
|
XSTATIC_MODULES = settings_utils.BASE_XSTATIC_MODULES
|
|
|
|
|
2016-11-30 11:15:36 +00:00
|
|
|
if not LOCAL_PATH:
|
|
|
|
LOCAL_PATH = os.path.join(ROOT_PATH, 'local')
|
|
|
|
LOCAL_SETTINGS_DIR_PATH = os.path.join(LOCAL_PATH, "local_settings.d")
|
|
|
|
|
|
|
|
_files = glob.glob(os.path.join(LOCAL_PATH, 'local_settings.conf'))
|
|
|
|
_files.extend(
|
|
|
|
sorted(glob.glob(os.path.join(LOCAL_SETTINGS_DIR_PATH, '*.conf'))))
|
|
|
|
_config = config.load_config(_files, ROOT_PATH, LOCAL_PATH)
|
|
|
|
|
|
|
|
# Apply the general configuration.
|
|
|
|
config.apply_config(_config, globals())
|
|
|
|
|
2011-01-12 21:43:31 +00:00
|
|
|
try:
|
2017-05-24 03:01:11 +00:00
|
|
|
from local.local_settings import * # noqa: F403,H303
|
2012-02-29 07:27:46 +00:00
|
|
|
except ImportError:
|
2017-06-01 02:36:38 +00:00
|
|
|
_LOG.warning("No local_settings file found.")
|
2011-01-12 21:43:31 +00:00
|
|
|
|
2016-11-30 11:15:36 +00:00
|
|
|
# configure templates
|
|
|
|
if not TEMPLATES[0]['DIRS']:
|
|
|
|
TEMPLATES[0]['DIRS'] = [os.path.join(ROOT_PATH, 'templates')]
|
|
|
|
|
2018-12-22 20:57:41 +00:00
|
|
|
TEMPLATES[0]['DIRS'] += ADD_TEMPLATE_DIRS
|
|
|
|
|
2016-08-04 19:41:46 +00:00
|
|
|
# configure template debugging
|
|
|
|
TEMPLATES[0]['OPTIONS']['debug'] = DEBUG
|
|
|
|
|
Pre-populate the Angular template cache and allow template overrides
This patch populates the Angular template cache from Django.
This eliminates the need for Angular to do an http get for every HTML
fragment.
In addition, now that we are filling the template cache, this patch
introduces the logic needed to override any Angular template HTML from
the current theme.
How it works:
A new template tag is created called "template_cache_preloads". This
tag is used in _scripts.html to generate a list of text/javascript
script tags, each one containing an Angular "run" method that loads
a template contents into the Angular template cache. The first time
any Horizon page is loaded after server start, the template cache
preloads are computed for the current theme.
The output of this tag is cached for 30 days in Django using the
"cache" tag. Further, that cached result is wrapped in a "compress js"
tag to collapse the individual <script> tags into 1 block of
javascript, and compress like all other javascript Horizon serves to
the client.
Finally, when using offline compression, the compressor evaluates the
nodelist (HTML content) of _scripts.html, notices the compress tag
and builds the template cache preloads for each possible theme. Later,
at runtime, when the preloads are generated for the current theme, the
compressor gets the result from the Django cache, and hashes the
contents to determine which manifest file to serve to the client.
Since the preloads generated at run-time are identical to those
generated off-line, the compressor hash matches an existing manifest
which is served to the client.
Notice that even though the template cache pre-loads are generated
off-line...the template_cache_preloads tag will be executed once
every 30 days anyway. However, since the result matches the off-line
compression, the existing manifest continues to be served to the client.
Finally, this patch ALSO watches for 'post_compress' signals. If it
detects that the angular template preloads have been re-compressed, it
clears the old version from the Django cache.
To test the template caching:
- Run horizon
- View page source
- Notice the new <script type="text/javascript"> tags contained in
the body (only visible if COMPRESS_ENABLED=False
- Open the javascript inspector
- Load launch instance
- Notice there are no longer http calls to load each HTML fragment
used by the Angular launch instance
To test the override:
- Set the DEFAULT_THEME='material'
- Create /horizon/openstack_dashboard/themes/material/\
static/templates/framework/widgets/help-panel/help-panel.html
- Set the content to <h1>TEST</h1>
- Run Horizon and open launch instance.
- The help content should contain "TEST"
To test the new template tag:
- set a breakpoint or print in angular.py:template_cache_preloads
and observe when it is called during off-line or run-time use
Co-Authored-By: Diana Whitten <hurgleburgler@gmail.com>
Implements: blueprint angular-template-overrides
Change-Id: I0e4e2623be58abbc68c6e02b2e9c5d7cdaba8e4d
2016-05-31 18:52:14 +00:00
|
|
|
# Template loaders
|
2016-03-23 15:10:12 +00:00
|
|
|
if DEBUG:
|
2016-08-04 19:41:46 +00:00
|
|
|
TEMPLATES[0]['OPTIONS']['loaders'].extend(
|
|
|
|
CACHED_TEMPLATE_LOADERS + ADD_TEMPLATE_LOADERS
|
|
|
|
)
|
2016-03-23 15:10:12 +00:00
|
|
|
else:
|
2016-08-04 19:41:46 +00:00
|
|
|
TEMPLATES[0]['OPTIONS']['loaders'].extend(
|
|
|
|
[('django.template.loaders.cached.Loader', CACHED_TEMPLATE_LOADERS)] +
|
|
|
|
ADD_TEMPLATE_LOADERS
|
|
|
|
)
|
2016-03-23 15:10:12 +00:00
|
|
|
|
2015-11-11 06:39:18 +00:00
|
|
|
# allow to drop settings snippets into a local_settings_dir
|
|
|
|
LOCAL_SETTINGS_DIR_PATH = os.path.join(ROOT_PATH, "local", "local_settings.d")
|
|
|
|
if os.path.exists(LOCAL_SETTINGS_DIR_PATH):
|
|
|
|
for (dirpath, dirnames, filenames) in os.walk(LOCAL_SETTINGS_DIR_PATH):
|
|
|
|
for filename in sorted(filenames):
|
|
|
|
if filename.endswith(".py"):
|
|
|
|
try:
|
2016-09-06 02:15:50 +00:00
|
|
|
with open(os.path.join(dirpath, filename)) as f:
|
2018-12-01 20:48:03 +00:00
|
|
|
# pylint: disable=exec-used
|
2016-09-06 02:15:50 +00:00
|
|
|
exec(f.read())
|
2019-12-15 18:52:13 +00:00
|
|
|
except Exception:
|
2017-06-01 02:36:38 +00:00
|
|
|
_LOG.exception(
|
2017-03-28 14:47:28 +00:00
|
|
|
"Can not exec settings snippet %s", filename)
|
2015-11-11 06:39:18 +00:00
|
|
|
|
2018-12-22 19:01:04 +00:00
|
|
|
if USER_MENU_LINKS is None:
|
|
|
|
USER_MENU_LINKS = []
|
2018-12-22 19:13:07 +00:00
|
|
|
if SHOW_OPENRC_FILE:
|
|
|
|
USER_MENU_LINKS.append({
|
2019-05-03 04:55:14 +00:00
|
|
|
'name': _('OpenStack RC File'),
|
2018-12-22 19:13:07 +00:00
|
|
|
'icon_classes': ['fa-download', ],
|
|
|
|
'url': 'horizon:project:api_access:openrc',
|
|
|
|
})
|
2018-12-22 19:01:04 +00:00
|
|
|
|
2014-04-11 11:50:34 +00:00
|
|
|
if not WEBROOT.endswith('/'):
|
|
|
|
WEBROOT += '/'
|
|
|
|
if LOGIN_URL is None:
|
|
|
|
LOGIN_URL = WEBROOT + 'auth/login/'
|
|
|
|
if LOGOUT_URL is None:
|
|
|
|
LOGOUT_URL = WEBROOT + 'auth/logout/'
|
2018-07-27 13:59:47 +00:00
|
|
|
if LOGIN_ERROR is None:
|
|
|
|
LOGIN_ERROR = WEBROOT + 'auth/error/'
|
2014-04-11 11:50:34 +00:00
|
|
|
if LOGIN_REDIRECT_URL is None:
|
|
|
|
LOGIN_REDIRECT_URL = WEBROOT
|
|
|
|
|
2015-12-17 06:16:35 +00:00
|
|
|
if MEDIA_ROOT is None:
|
|
|
|
MEDIA_ROOT = os.path.abspath(os.path.join(ROOT_PATH, '..', 'media'))
|
|
|
|
|
|
|
|
if MEDIA_URL is None:
|
|
|
|
MEDIA_URL = WEBROOT + 'media/'
|
2015-07-13 17:40:32 +00:00
|
|
|
|
|
|
|
if STATIC_ROOT is None:
|
|
|
|
STATIC_ROOT = os.path.abspath(os.path.join(ROOT_PATH, '..', 'static'))
|
|
|
|
|
2015-07-21 17:16:22 +00:00
|
|
|
if STATIC_URL is None:
|
|
|
|
STATIC_URL = WEBROOT + 'static/'
|
|
|
|
|
2016-05-17 22:06:05 +00:00
|
|
|
AVAILABLE_THEMES, SELECTABLE_THEMES, DEFAULT_THEME = (
|
|
|
|
theme_settings.get_available_themes(
|
|
|
|
AVAILABLE_THEMES,
|
|
|
|
DEFAULT_THEME,
|
|
|
|
SELECTABLE_THEMES
|
|
|
|
)
|
Dynamic Themes
Horizon themes are now configurable at a user level, through the use
of cookies. The themes that can be set are configurable at a
deployment level through settings.py. Horizon can be configured to
run with multiple themes, and allow users to choose which themes
they wish to run.
Django Compressor:
In order to support dynamic themes, each theme configuration must
be pre-compiled through the Django compressor. By making use of its
built in COMPRESS_OFFLINE_CONTEXT, we now return a generator to
create each of the theme's necessary offline contexts.
Templates:
Horizon themes allowed template overrides via their 'templates'
subfolder. In order to maintain this parity, a custom theme template
loader was created. It is run before the other loads, and simply
looks for a Django template in the current theme (cookie driven)
before diverting to the previous template loaders.
Static Files:
Horizon themes allowed static overrides of the images in
'dashboard/img' folder. A template tag, 'themable_asset' was created
to maintain this parity. Any asset that is wished to be made themable,
given that it is located in Horizon's 'static/dashboard' folder, can
now be made ot be themable. By making this a template tag, this
gives the developers more granular control over what branders can
customize.
Angular and Plugins:
By far, the trickiest part of this task, Angular and Plugins are
dynamic in the files that they 'discover'. SCSS is not flexible in
this manner at ALL. SCSS disallows the importation of a variable
name. To get around this, themes.scss was created as a Django
template. This template is the top level import file for all styles
within Horizon, and therefore, allows ALL the scss files to share a
common namespace and thus, can use shared variables as well as extend
shared styles.
Other:
This change is fundamental, in that it changes the method by which
Horizon ingests its SCSS files. Many problems existing in the
previous implementation, in an effort to make Horizon flexible, its
SCSS was made very inflexible. This patch corrects those problems.
Change-Id: Ic48b4b5c1d1a41f1e01a8d52784c9d38d192c8f1
Implements: blueprint horizon-dynamic-theme
Closes-Bug: #1480427
2016-01-30 16:42:15 +00:00
|
|
|
)
|
2015-06-03 19:46:51 +00:00
|
|
|
|
2018-03-13 03:00:15 +00:00
|
|
|
# Discover all the directories that contain static files
|
|
|
|
STATICFILES_DIRS = theme_settings.get_theme_static_dirs(
|
2015-07-22 17:15:45 +00:00
|
|
|
AVAILABLE_THEMES, THEME_COLLECTION_DIR, ROOT_PATH)
|
2015-06-20 01:21:30 +00:00
|
|
|
|
2015-08-05 18:00:00 +00:00
|
|
|
# Ensure that we always have a SECRET_KEY set, even when no local_settings.py
|
|
|
|
# file is present. See local_settings.py.example for full documentation on the
|
|
|
|
# horizon.utils.secret_key module and its use.
|
|
|
|
if not SECRET_KEY:
|
|
|
|
if not LOCAL_PATH:
|
|
|
|
LOCAL_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
|
|
|
'local')
|
|
|
|
|
2018-12-01 21:21:02 +00:00
|
|
|
# pylint: disable=ungrouped-imports
|
2015-08-05 18:00:00 +00:00
|
|
|
from horizon.utils import secret_key
|
|
|
|
SECRET_KEY = secret_key.generate_or_read_from_file(os.path.join(LOCAL_PATH,
|
|
|
|
'.secret_key_store'))
|
|
|
|
|
2015-07-22 17:15:45 +00:00
|
|
|
# populate HORIZON_CONFIG with auto-discovered JavaScript sources, mock files,
|
|
|
|
# specs files and external templates.
|
|
|
|
settings_utils.find_static_files(HORIZON_CONFIG, AVAILABLE_THEMES,
|
|
|
|
THEME_COLLECTION_DIR, ROOT_PATH)
|
|
|
|
|
2013-11-14 11:43:05 +00:00
|
|
|
INSTALLED_APPS = list(INSTALLED_APPS) # Make sure it's mutable
|
2015-07-22 17:15:45 +00:00
|
|
|
settings_utils.update_dashboards(
|
2015-03-04 17:38:33 +00:00
|
|
|
[
|
2018-04-11 09:57:28 +00:00
|
|
|
enabled,
|
|
|
|
local_enabled,
|
2015-03-04 17:38:33 +00:00
|
|
|
],
|
|
|
|
HORIZON_CONFIG,
|
|
|
|
INSTALLED_APPS,
|
|
|
|
)
|
2014-07-17 15:19:52 +00:00
|
|
|
INSTALLED_APPS[0:0] = ADD_INSTALLED_APPS
|
2013-11-14 11:43:05 +00:00
|
|
|
|
2018-03-13 03:00:15 +00:00
|
|
|
# Include xstatic_modules specified in plugin
|
|
|
|
XSTATIC_MODULES += HORIZON_CONFIG['xstatic_modules']
|
|
|
|
|
|
|
|
# Discover all the xstatic module entry points to embed in our HTML
|
|
|
|
STATICFILES_DIRS += settings_utils.get_xstatic_dirs(
|
|
|
|
XSTATIC_MODULES, HORIZON_CONFIG)
|
|
|
|
|
Dynamic Themes
Horizon themes are now configurable at a user level, through the use
of cookies. The themes that can be set are configurable at a
deployment level through settings.py. Horizon can be configured to
run with multiple themes, and allow users to choose which themes
they wish to run.
Django Compressor:
In order to support dynamic themes, each theme configuration must
be pre-compiled through the Django compressor. By making use of its
built in COMPRESS_OFFLINE_CONTEXT, we now return a generator to
create each of the theme's necessary offline contexts.
Templates:
Horizon themes allowed template overrides via their 'templates'
subfolder. In order to maintain this parity, a custom theme template
loader was created. It is run before the other loads, and simply
looks for a Django template in the current theme (cookie driven)
before diverting to the previous template loaders.
Static Files:
Horizon themes allowed static overrides of the images in
'dashboard/img' folder. A template tag, 'themable_asset' was created
to maintain this parity. Any asset that is wished to be made themable,
given that it is located in Horizon's 'static/dashboard' folder, can
now be made ot be themable. By making this a template tag, this
gives the developers more granular control over what branders can
customize.
Angular and Plugins:
By far, the trickiest part of this task, Angular and Plugins are
dynamic in the files that they 'discover'. SCSS is not flexible in
this manner at ALL. SCSS disallows the importation of a variable
name. To get around this, themes.scss was created as a Django
template. This template is the top level import file for all styles
within Horizon, and therefore, allows ALL the scss files to share a
common namespace and thus, can use shared variables as well as extend
shared styles.
Other:
This change is fundamental, in that it changes the method by which
Horizon ingests its SCSS files. Many problems existing in the
previous implementation, in an effort to make Horizon flexible, its
SCSS was made very inflexible. This patch corrects those problems.
Change-Id: Ic48b4b5c1d1a41f1e01a8d52784c9d38d192c8f1
Implements: blueprint horizon-dynamic-theme
Closes-Bug: #1480427
2016-01-30 16:42:15 +00:00
|
|
|
# This base context objects gets added to the offline context generator
|
|
|
|
# for each theme configured.
|
|
|
|
HORIZON_COMPRESS_OFFLINE_CONTEXT_BASE = {
|
2015-07-21 17:45:40 +00:00
|
|
|
'WEBROOT': WEBROOT,
|
2013-01-16 12:39:01 +00:00
|
|
|
'STATIC_URL': STATIC_URL,
|
Pre-populate the Angular template cache and allow template overrides
This patch populates the Angular template cache from Django.
This eliminates the need for Angular to do an http get for every HTML
fragment.
In addition, now that we are filling the template cache, this patch
introduces the logic needed to override any Angular template HTML from
the current theme.
How it works:
A new template tag is created called "template_cache_preloads". This
tag is used in _scripts.html to generate a list of text/javascript
script tags, each one containing an Angular "run" method that loads
a template contents into the Angular template cache. The first time
any Horizon page is loaded after server start, the template cache
preloads are computed for the current theme.
The output of this tag is cached for 30 days in Django using the
"cache" tag. Further, that cached result is wrapped in a "compress js"
tag to collapse the individual <script> tags into 1 block of
javascript, and compress like all other javascript Horizon serves to
the client.
Finally, when using offline compression, the compressor evaluates the
nodelist (HTML content) of _scripts.html, notices the compress tag
and builds the template cache preloads for each possible theme. Later,
at runtime, when the preloads are generated for the current theme, the
compressor gets the result from the Django cache, and hashes the
contents to determine which manifest file to serve to the client.
Since the preloads generated at run-time are identical to those
generated off-line, the compressor hash matches an existing manifest
which is served to the client.
Notice that even though the template cache pre-loads are generated
off-line...the template_cache_preloads tag will be executed once
every 30 days anyway. However, since the result matches the off-line
compression, the existing manifest continues to be served to the client.
Finally, this patch ALSO watches for 'post_compress' signals. If it
detects that the angular template preloads have been re-compressed, it
clears the old version from the Django cache.
To test the template caching:
- Run horizon
- View page source
- Notice the new <script type="text/javascript"> tags contained in
the body (only visible if COMPRESS_ENABLED=False
- Open the javascript inspector
- Load launch instance
- Notice there are no longer http calls to load each HTML fragment
used by the Angular launch instance
To test the override:
- Set the DEFAULT_THEME='material'
- Create /horizon/openstack_dashboard/themes/material/\
static/templates/framework/widgets/help-panel/help-panel.html
- Set the content to <h1>TEST</h1>
- Run Horizon and open launch instance.
- The help content should contain "TEST"
To test the new template tag:
- set a breakpoint or print in angular.py:template_cache_preloads
and observe when it is called during off-line or run-time use
Co-Authored-By: Diana Whitten <hurgleburgler@gmail.com>
Implements: blueprint angular-template-overrides
Change-Id: I0e4e2623be58abbc68c6e02b2e9c5d7cdaba8e4d
2016-05-31 18:52:14 +00:00
|
|
|
'HORIZON_CONFIG': HORIZON_CONFIG,
|
2019-04-22 02:47:28 +00:00
|
|
|
'NG_TEMPLATE_CACHE_AGE': NG_TEMPLATE_CACHE_AGE if not DEBUG else 0,
|
2013-01-16 12:39:01 +00:00
|
|
|
}
|
2012-11-21 04:33:09 +00:00
|
|
|
|
2013-02-06 00:04:10 +00:00
|
|
|
if DEBUG:
|
|
|
|
logging.basicConfig(level=logging.DEBUG)
|
2016-05-19 15:15:24 +00:00
|
|
|
|
2020-02-18 15:24:03 +00:00
|
|
|
|
2016-05-19 15:15:24 +00:00
|
|
|
# Here comes the Django settings deprecation section. Being at the very end
|
|
|
|
# of settings.py allows it to catch the settings defined in local_settings.py
|
|
|
|
# or inside one of local_settings.d/ snippets.
|