diff --git a/horizon/templates/horizon/jasmine/jasmine.html b/horizon/templates/horizon/jasmine/jasmine.html
index 77940d8311..584471d64f 100644
--- a/horizon/templates/horizon/jasmine/jasmine.html
+++ b/horizon/templates/horizon/jasmine/jasmine.html
@@ -8,24 +8,15 @@
window.STATIC_URL = '/static/';
window.WEBROOT = '/';
+
-
-
-
-
-
+ {% for file in HORIZON_CONFIG.xstatic_lib_files %}
+
+ {% endfor %}
-
-
-
-
-
-
-
-
-
-
-
-
-
+{% for file in HORIZON_CONFIG.xstatic_lib_files %}
+
+{% endfor %}
{% endcompress %}
diff --git a/openstack_dashboard/templates/horizon/_scripts.html b/openstack_dashboard/templates/horizon/_scripts.html
index f211f80dbc..ba400aa542 100644
--- a/openstack_dashboard/templates/horizon/_scripts.html
+++ b/openstack_dashboard/templates/horizon/_scripts.html
@@ -11,28 +11,13 @@
{% include "horizon/_script_i18n.html" %}
-{% comment %} Compress jQuery, Angular, Plugins, Bootstrap, Hogan.js and Horizon-specific JS. {% endcomment %}
+{% comment %} Compress Horizon-specific JS. {% endcomment %}
{% compress js %}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -60,15 +45,10 @@
-
-
-
-
-
{% for file in HORIZON_CONFIG.js_files %}
diff --git a/openstack_dashboard/test/settings.py b/openstack_dashboard/test/settings.py
index 40866b72ee..298703e3fe 100644
--- a/openstack_dashboard/test/settings.py
+++ b/openstack_dashboard/test/settings.py
@@ -16,8 +16,6 @@ from django.utils.translation import pgettext_lazy
from horizon.test.settings import * # noqa
from horizon.utils import secret_key
from openstack_dashboard import exceptions
-from openstack_dashboard.static_settings import find_static_files # noqa
-from openstack_dashboard.static_settings import get_staticfiles_dirs # noqa
from horizon.utils.escape import monkeypatch_escape
@@ -25,7 +23,7 @@ from horizon.utils.escape import monkeypatch_escape
# enabling in our test setup to find any issues it might cause
monkeypatch_escape()
-STATICFILES_DIRS = get_staticfiles_dirs()
+from openstack_dashboard.utils import settings as settings_utils
TEST_DIR = os.path.dirname(os.path.abspath(__file__))
ROOT_PATH = os.path.abspath(os.path.join(TEST_DIR, ".."))
@@ -102,14 +100,19 @@ ANGULAR_FEATURES = {
'images_panel': False # Use the legacy panel so unit tests are still run
}
+STATICFILES_DIRS = settings_utils.get_xstatic_dirs(
+ settings_utils.BASE_XSTATIC_MODULES, HORIZON_CONFIG
+)
+
# Load the pluggable dashboard settings
import openstack_dashboard.enabled
-from openstack_dashboard.utils import settings
+import openstack_dashboard.local.enabled
INSTALLED_APPS = list(INSTALLED_APPS) # Make sure it's mutable
-settings.update_dashboards(
+settings_utils.update_dashboards(
[
openstack_dashboard.enabled,
+ openstack_dashboard.local.enabled
],
HORIZON_CONFIG,
INSTALLED_APPS,
@@ -119,8 +122,8 @@ settings.update_dashboards(
# the stacks MappingsTests are updated with the new URL path.
HORIZON_CONFIG['swift_panel'] = 'legacy'
-find_static_files(HORIZON_CONFIG, AVAILABLE_THEMES,
- THEME_COLLECTION_DIR, ROOT_PATH)
+settings_utils.find_static_files(HORIZON_CONFIG, AVAILABLE_THEMES,
+ THEME_COLLECTION_DIR, ROOT_PATH)
# Set to 'legacy' or 'direct' to allow users to upload images to glance via
# Horizon server. When enabled, a file form field will appear on the create
diff --git a/openstack_dashboard/themes/material/static/bootstrap/_styles.scss b/openstack_dashboard/themes/material/static/bootstrap/_styles.scss
index 4c1f60264d..5085450102 100644
--- a/openstack_dashboard/themes/material/static/bootstrap/_styles.scss
+++ b/openstack_dashboard/themes/material/static/bootstrap/_styles.scss
@@ -1,7 +1,7 @@
// Based on Paper
// Bootswatch
// -----------------------------------------------------
-@import "/bootstrap/scss/bootstrap/mixins/vendor-prefixes";
+@import "/horizon/lib/bootstrap_scss/scss/bootstrap/mixins/vendor-prefixes";
@import "/horizon/lib/bootswatch/paper/bootswatch";
@import "/horizon/lib/roboto_fontface/css/roboto-fontface.scss";
diff --git a/openstack_dashboard/utils/settings.py b/openstack_dashboard/utils/settings.py
index b21ffd2767..a4abaec5f5 100644
--- a/openstack_dashboard/utils/settings.py
+++ b/openstack_dashboard/utils/settings.py
@@ -17,7 +17,8 @@ import os
import pkgutil
import six
-from horizon.utils import file_discovery as fd
+from horizon.utils import file_discovery
+from openstack_dashboard import theme_settings
def import_submodules(module):
@@ -121,7 +122,8 @@ def update_dashboards(modules, horizon_config, installed_apps):
for _app in _apps:
module = import_module(_app)
base_path = os.path.join(module.__path__[0], 'static/')
- fd.populate_horizon_config(horizon_config, base_path)
+ file_discovery.populate_horizon_config(horizon_config,
+ base_path)
add_exceptions = six.iteritems(config.get('ADD_EXCEPTIONS', {}))
for category, exc_list in add_exceptions:
@@ -168,3 +170,173 @@ def update_dashboards(modules, horizon_config, installed_apps):
# so we save the reference to it before we append to installed_apps
horizon_config.setdefault('plugins', []).extend(apps)
installed_apps[0:0] = apps
+
+
+# Order matters, list the xstatic module name and the entry point file(s) for
+# that module (this is often defined as the "main" in bower.json, and
+# as the xstatic module MAIN variable in the very few compliant xstatic
+# modules). If the xstatic module does define a MAIN then set the files
+# list to None.
+# This list is to be used as the base list which is potentially added to in
+# local_settings.py before being passed to get_xstatic_dirs()
+BASE_XSTATIC_MODULES = [
+ ('xstatic.pkg.jquery', ['jquery.js']),
+ ('xstatic.pkg.jquery_migrate', ['jquery-migrate.js']),
+ ('xstatic.pkg.angular', [
+ 'angular.js',
+ 'angular-cookies.js',
+ 'angular-sanitize.js',
+ 'angular-route.js'
+ ]),
+ ('xstatic.pkg.angular_bootstrap', ['angular-bootstrap.js']),
+ ('xstatic.pkg.angular_gettext', ['angular-gettext.js']),
+ ('xstatic.pkg.angular_lrdragndrop', None),
+ ('xstatic.pkg.angular_smart_table', None),
+ ('xstatic.pkg.angular_fileupload', ['ng-file-upload-all.js']),
+ ('xstatic.pkg.d3', ['d3.js']),
+ ('xstatic.pkg.jquery_quicksearch', ['jquery.quicksearch.js']),
+ ('xstatic.pkg.jquery_tablesorter', ['jquery.tablesorter.js']),
+ ('xstatic.pkg.spin', ['spin.js', 'spin.jquery.js']),
+ ('xstatic.pkg.jquery_ui', ['jquery-ui.js']),
+ ('xstatic.pkg.bootstrap_scss', ['js/bootstrap.js']),
+ ('xstatic.pkg.bootstrap_datepicker', ['bootstrap-datepicker.js']),
+ ('xstatic.pkg.hogan', ['hogan.js']),
+ ('xstatic.pkg.rickshaw', ['rickshaw.js']),
+ ('xstatic.pkg.jsencrypt', ['jsencrypt.js']),
+ ('xstatic.pkg.objectpath', ['ObjectPath.js']),
+ ('xstatic.pkg.tv4', ['tv4.js']),
+ ('xstatic.pkg.angular_schema_form', ['schema-form.js']),
+
+ # @imported in scss files diectly
+ ('xstatic.pkg.font_awesome', []),
+ ('xstatic.pkg.bootswatch', []),
+ ('xstatic.pkg.roboto_fontface', []),
+ ('xstatic.pkg.mdi', []),
+
+ # testing only, not included in application
+ ('xstatic.pkg.jasmine', []),
+ ('xstatic.pkg.termjs', []),
+]
+
+
+def get_xstatic_dirs(XSTATIC_MODULES, HORIZON_CONFIG):
+ """Discover static file configuration of the xstatic modules.
+
+ For each entry in the XSTATIC_MODULES list we determine the entry
+ point files (which may come from the xstatic MAIN var) and then
+ determine where in the Django static tree the xstatic package's contents
+ should be placed.
+
+ For jquery.bootstrap.wizard.js the module name is None the static file is
+ actually a 3rd-party file but resides in the Horizon source tree and not
+ an xstatic package.
+
+ The xstatic.pkg.jquery_ui package had its contents moved by packagers so
+ it must be handled as a special case.
+ """
+ STATICFILES_DIRS = []
+ HORIZON_CONFIG['xstatic_lib_files'] = []
+ for module_name, files in XSTATIC_MODULES:
+ module = import_module(module_name)
+ if module_name == 'xstatic.pkg.jquery_ui':
+ # determine the correct path for jquery-ui which packagers moved
+ if module.VERSION.startswith('1.10.'):
+ # The 1.10.x versions already contain 'ui' directory.
+ files = ['ui/' + files[0]]
+
+ STATICFILES_DIRS.append(
+ ('horizon/lib/' + module.NAME, module.BASE_DIR)
+ )
+
+ # pull the file entry points from the xstatic package MAIN if possible
+ if hasattr(module, 'MAIN'):
+ files = module.MAIN
+ if not isinstance(files, list):
+ files = [files]
+
+ # just the Javascript files, please (don't