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.