A stable and custom theme for Horizon
During the OpenStack Icehouse development cycle, we gained in Horizon the ability to use additional python packages by simply dropping in a file in the openstack_dashboard/enabled dir.
In the rest of the article, I'll propose a method to customize the look and feel, without breaking during package updates. It will work, if you have a Horizon package in Icehouse release RC1 or later.
Fedora and RDO openstack-dashboard packages are installing the Dashboard into /usr/share/openstack-dashboard.
First of all, we'll add a config openstack_dashboard/enabled/_99_theme.py
# The name of the dashboard to be added to HORIZON['dashboards']. Required.
DASHBOARD = 'theme'
# If set to True, this dashboard will be set as the default dashboard.
DEFAULT = False
# A dictionary of exception classes to be added to HORIZON['exceptions'].
ADD_EXCEPTIONS = {}
# A list of applications to be added to INSTALLED_APPS.
ADD_INSTALLED_APPS = ['openstack_dashboard.dashboards.theme']
There are two notable settings: DASHBOARD = 'theme' - this is the name of the added dashboard. We'll come back to this later. The other one is ADD_INSTALLED_APPS = ['openstack_dashboard.dashboards.theme']. This is a python module, which can reside everywhere in the file system (as long as it's in the python search path). I chose to put it directly below openstack_dashboard/dashboards in a dir named theme
Actually, I put a very minimum set of files into the theme directory:
theme/ |-- dashboard.py |-- __init__.py |-- models.py |-- static | `-- dashboard | |-- css | | `-- font-awesome.min.css | |-- fonts | | |-- FontAwesome.otf | | |-- OpenSans-Regular-webfont.eot | | |-- OpenSans-Regular-webfont.svg | | |-- OpenSans-Regular-webfont.ttf | | `-- OpenSans-Regular-webfont.woff | |-- img | | |-- bg-login.jpg | | |-- brand.svg | | `-- logo.svg | `-- less | |-- rcue | | |-- fonts.less | | |-- icons.less | | |-- login.less | | |-- navbar.less | `-- theme.less |-- templates | |-- auth | | |-- _login.html | | `-- login.html | |-- base.html | |-- _header.html | |-- horizon | | |-- common | | | `-- _sidebar.html | | |-- _nav_list.html | | `-- _subnav_list.html | |-- splash.html | `-- _stylesheets.html `-- theme_index |-- __init__.py |-- __init__.pyc |-- panel.py |-- panel.pyc |-- urls.py |-- urls.pyc |-- views.py `-- views.pyc
At first glance, that looks way more than it actually is. While most files are self-explanatory, I'll go into details with a few files.
theme-directory
The files __init__.py and models.py may be completely empty, or just contain a comment:
# intentionally left blank
The more interesting file is dashboard.py:
import horizon
class Theme(horizon.Dashboard):
name = _("theme")
slug = "theme"
panels = ('theme_index', )
default_panel = 'theme_index'
nav = False
horizon.register(Theme)
Important to note is the option nav = False, which prevents this dashboard to show up in the navigation bar; this is used e.g for contents to be linked manually, like "Settings" or not to be linked at all, like a theme.
theme_index
As above in the theme dir, __init__.py and views.py may be completely empty. urls.py is nearly empty as well:
urlpatterns = ()
panels.py:
import horizon
from openstack_dashboard.dashboards.theme import dashboard
class ThemePanel(horizon.Panel):
name = "Panel providing a theme"
slug = 'theme_index'
nav = True
dashboard.Theme.register(ThemePanel)
This code snippet should be enough for Horizon to think, there is a real dashboard, which just should not be included in the automatically generated navigation.
Putting all together
Now we can put templates into the templates directory, and static files i to static. When Horizon delivers web pages, based on templates, it will search for the them in theme/templates first; if found there, they will be delivered, if not, Horizon will fall back to the default pages found in the other templates dirs in horizon source tree.
As a starter, just copy e.g base.html or _stylesheets.html from horizon/templates directory, to templates and modify them.
When adding static files, you need to issue
from /usr/share/openstack-dashboard to collect them and to copy static files to /usr/share/openstack-dashboard/static.