Horizon Themes should support and house easy Template Overrides

Themers need an easy way to maninpulate existing templates.  Django
provides a very easy method for custom overloads via prepending a
Theme located template directory to the existing TEMPLATE_DIRS tuple
located in the settings.py.  The new template directory needs to
maintain the same directory structure as the overridden templates.

Also, the entire theme directory was being served out at /static/custom,
but it is not needed to statically serve up the Django templates.
This new functionality now supports having more granular control
over what is statically served from /static/custom by specifying a
'static' subfolder within your theme.  If no sub folder is found, then
the legacy functionality is the fall back, and the entire folder is served.

Since the Blue theme was provided as a way to showcase the extensibility
of Horizon themes, I have provided an examle of what a template override
would look like and how it would be used.  The Blue theme now includes an
entirely Bootstrap Compliant top navbar experience that is responsive, but
without losing any of the underlying Django functionality, values or
translations.

Implements: blueprint horizon-theme-templates
Change-Id: I40a744d5439ee5138337a7cdb1c0a937b5107836
This commit is contained in:
Diana Whitten 2015-06-03 12:46:51 -07:00
parent eff59ac2dd
commit 7020bf3cb9
12 changed files with 120 additions and 1 deletions

View File

@ -401,11 +401,15 @@ This example sorts flavors by vcpus in descending order::
Default: ``"static/themes/default"``
This setting allows Horizon to use a custom theme. The theme folder
should contains one _variables.scss file and one _styles.scss file.
should contain one _variables.scss file and one _styles.scss file.
_variables.scss contains all the bootstrap and horizon specific variables
which are used to style the GUI. Whereas _styles.scss contains extra styling.
For example themes, see: /horizon/openstack_dashboard/static/themes/
If the static theme folder also contains a sub-folder 'templates', then
the path to that sub-folder will be prepended to TEMPLATE_DIRS tuple
to allow for theme specific template customizations.
``DROPDOWN_MAX_ITEMS``
----------------------

View File

@ -283,6 +283,19 @@ STATIC_URL = WEBROOT + 'static/'
STATICFILES_DIRS = get_staticfiles_dirs(WEBROOT)
CUSTOM_THEME = os.path.join(ROOT_PATH, CUSTOM_THEME_PATH)
# If a custom template directory exists within our custom theme, then prepend
# it to our first-come, first-serve TEMPLATE_DIRS
if os.path.exists(os.path.join(CUSTOM_THEME, 'templates')):
TEMPLATE_DIRS = \
(os.path.join(CUSTOM_THEME_PATH, 'templates'),) + TEMPLATE_DIRS
# Only expose the subdirectory 'static' if it exists from a custom theme,
# allowing other logic to live with a theme that we might not want to expose
# statically
if os.path.exists(os.path.join(CUSTOM_THEME, 'static')):
CUSTOM_THEME = os.path.join(CUSTOM_THEME, 'static')
STATICFILES_DIRS.append(
('custom', CUSTOM_THEME),
)

View File

@ -0,0 +1,71 @@
{% load branding i18n %}
{% load url from future %}
{% load context_selection %}
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse">
<span class="sr-only">{% trans "Toggle navigation" %}</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="{% site_branding_link %}">
<span class="openstack-logo"></span>
{% site_branding %}
</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="navbar-collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
{% show_overview %}
<b class="caret"></b>
</a>
{% show_domain_list %}
{% show_project_list %}
{% show_region_list %}
</li>
</ul>
{% include "horizon/common/_region_selector.html" %}
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
<span class="fa fa-user"></span>
{{ request.user.username }}
<b class="caret"></b>
</a>
<ul id="editor_list" class="dropdown-menu">
<li>
<a href="{% url 'horizon:settings:user:index' %}">
<span class="fa fa-cog"></span>
{% trans "Settings" %}
</a>
</li>
{% if HORIZON_CONFIG.help_url %}
<li>
<a href="{{ HORIZON_CONFIG.help_url }}" target="_blank">
<span class="fa fa-question-circle"></span>
{% trans "Help" %}
</a>
</li>
{% endif %}
<li class="divider"></li>
<li>
<a href="{% url 'logout' %}">
<span class="fa fa-sign-out"></span>
{% trans "Sign Out" %}
</a>
</li>
</ul>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>

View File

@ -0,0 +1,12 @@
<span class="fa fa-list-alt"></span>
<span class="context-overview">
{% if domain_supported %}
<span>{{ domain_name }}</span>
<strong>&middot;</strong>
{% endif %}
<span>{{ project_name }}</span>
{% if multi_region %}
<strong>&middot;</strong>
<span>{{ region_name }}</span>
{% endif %}
</span>

View File

@ -0,0 +1,18 @@
{% load i18n %}
{% load url from future %}
{% with dashboard_url=request.horizon.dashboard.get_absolute_url %}
<ul class="dropdown-menu">
<li class="dropdown-header">{% trans "Projects:" %}</li>
{% for project in projects %}
<li>
<a href="{% url 'switch_tenants' project.id %}?next={{ dashboard_url }}">
{{ project.name }}
{% if project.enabled and project.id == project_id %}
<span class="fa fa-check"></span>
{% endif %}
</a>
</li>
{% endfor %}
</ul>
{% endwith %}