summaryrefslogtreecommitdiff
path: root/doc/source/configuration/customizing.rst
blob: bff71f3866c54e06d5ea9a8c36137f9fa60ce307 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
.. _install-customizing:

===================
Customizing Horizon
===================

.. seealso::

   You may also be interested in :doc:`themes` and :doc:`branding`.

Changing the Site Title
=======================

The OpenStack Dashboard Site Title branding (i.e. "**OpenStack** Dashboard")
can be overwritten by adding the attribute ``SITE_BRANDING``
to ``local_settings.py`` with the value being the desired name.

The file ``local_settings.py`` can be found at the Horizon directory path of
``openstack_dashboard/local/local_settings.py``.

Changing the Brand Link
=======================

The logo also acts as a hyperlink. The default behavior is to redirect to
``horizon:user_home``. By adding the attribute ``SITE_BRANDING_LINK`` with
the desired url target e.g., ``http://sample-company.com`` in
``local_settings.py``, the target of the hyperlink can be changed.

Customizing the Footer
======================

It is possible to customize the global and login footers using a theme's
template override.  Simply add ``_footer.html`` for a global footer
override or ``_login_footer.html`` for the login page's footer to your
theme's template directory.

Modifying Existing Dashboards and Panels
========================================

If you wish to alter dashboards or panels which are not part of your codebase,
you can specify a custom python module which will be loaded after the entire
Horizon site has been initialized, but prior to the URLconf construction.
This allows for common site-customization requirements such as:

* Registering or unregistering panels from an existing dashboard.
* Changing the names of dashboards and panels.
* Re-ordering panels within a dashboard or panel group.

Default Horizon panels are loaded based upon files within the
openstack_dashboard/enabled/ folder. These files are loaded based upon the
filename order, with space left for more files to be added. There are some
example files available within this folder, with the .example suffix
added. Developers and deployers should strive to use this method of
customization as much as possible, and support for this is given preference
over more exotic methods such as monkey patching and overrides files.

.. _horizon-customization-module:

Horizon customization module (overrides)
========================================

Horizon has a global overrides mechanism available to perform customizations
that are not yet customizable via configuration settings. This file can perform
monkey patching and other forms of customization which are not possible via the
enabled folder's customization method.

This method of customization is meant to be available for deployers of Horizon,
and use of this should be avoided by Horizon plugins at all cost. Plugins
needing this level of monkey patching and flexibility should instead look for
changing their __init__.py file and performing customizations through other
means.

To specify the python module containing your modifications, add the key
``customization_module`` to your ``HORIZON_CONFIG`` dictionary in
``local_settings.py``. The value should be a string containing the path to your
module in dotted python path notation. Example::

    HORIZON_CONFIG["customization_module"] = "my_project.overrides"

You can do essentially anything you like in the customization module. For
example, you could change the name of a panel::

    from django.utils.translation import ugettext_lazy as _

    import horizon

    # Rename "User Settings" to "User Options"
    settings = horizon.get_dashboard("settings")
    user_panel = settings.get_panel("user")
    user_panel.name = _("User Options")

Or get the instances panel::

    projects_dashboard = horizon.get_dashboard("project")
    instances_panel = projects_dashboard.get_panel("instances")

Or just remove it entirely::

    projects_dashboard.unregister(instances_panel.__class__)

You cannot unregister a ``default_panel``. If you wish to remove a
``default_panel``, you need to make a different panel in the dashboard as a
``default_panel`` and then unregister the former. For example, if you wished
to remove the ``overview_panel`` from the ``Project`` dashboard, you could do
the following::

    project = horizon.get_dashboard('project')
    project.default_panel = "instances"
    overview = project.get_panel('overview')
    project.unregister(overview.__class__)

You can also override existing methods with your own versions::

    from openstack_dashboard.dashboards.admin.info import tabs
    from openstack_dashboard.dashboards.project.instances import tables

    NO = lambda *x: False

    tables.AssociateIP.allowed = NO
    tables.SimpleAssociateIP.allowed = NO
    tables.SimpleDisassociateIP.allowed = NO

You could also customize what columns are displayed in an existing
table, by redefining the ``columns`` attribute of its ``Meta``
class. This can be achieved in 3 steps:

#. Extend the table that you wish to modify
#. Redefine the ``columns`` attribute under the ``Meta`` class for this
   new table
#. Modify the ``table_class`` attribute for the related view so that it
   points to the new table


For example, if you wished to remove the Admin State column from the
:class:`~openstack_dashboard.dashboards.admin.networks.tables.NetworksTable`,
you could do the following::

    from openstack_dashboard.dashboards.project.networks import tables
    from openstack_dashboard.dashboards.project.networks import views

    class MyNetworksTable(tables.NetworksTable):

        class Meta(tables.NetworksTable.Meta):
            columns = ('name', 'subnets', 'shared', 'status')

    views.IndexView.table_class = MyNetworksTable

If you want to add a column you can override the parent table in a
similar way, add the new column definition and then use the ``Meta``
``columns`` attribute to control the column order as needed.

.. NOTE::

    ``my_project.overrides`` needs to be importable by the python process running
    Horizon.
    If your module is not installed as a system-wide python package,
    you can either make it installable (e.g., with a setup.py)
    or you can adjust the python path used by your WSGI server to include its location.

    Probably the easiest way is to add a ``python-path`` argument to
    the ``WSGIDaemonProcess`` line in Apache's Horizon config.

    Assuming your ``my_project`` module lives in ``/opt/python/my_project``,
    you'd make it look like the following::

        WSGIDaemonProcess [... existing options ...] python-path=/opt/python


Customize the project and user table columns
============================================


Keystone V3 has a place to store extra information regarding project and user.
Using the override mechanism described in :ref:`horizon-customization-module`,
Horizon is able to show these extra information as a custom column.
For example, if a user in Keystone has an attribute ``phone_num``, you could
define new column::

    from django.utils.translation import ugettext_lazy as _

    from horizon import forms
    from horizon import tables

    from openstack_dashboard.dashboards.identity.users import tables as user_tables
    from openstack_dashboard.dashboards.identity.users import views

    class MyUsersTable(user_tables.UsersTable):
        phone_num = tables.Column('phone_num',
                                  verbose_name=_('Phone Number'),
                                  form_field=forms.CharField(),)

        class Meta(user_tables.UsersTable.Meta):
            columns = ('name', 'description', 'phone_num')

    views.IndexView.table_class = MyUsersTable


Customize Angular dashboards
============================

In Angular, you may write a plugin to extend certain features. Two components
in the Horizon framework that make this possible are the extensibility service
and the resource type registry service. The ``extensibleService`` allows
certain Horizon elements to be extended dynamically, including add, remove, and
replace. The ``resourceTypeRegistry`` service provides methods to set and get
information pertaining to a resource type object. We use Heat type names like
``OS::Glance::Image`` as our reference name.

Some information you may place in the registry include:

* API to fetch data from
* Property names
* Actions (e.g. "Create Volume")
* URL paths to detail view or detail drawer
* Property information like labels or formatting for property values

These properties in the registry use the extensibility service (as of Newton
release):

* globalActions
* batchActions
* itemActions
* detailViews
* tableColumns
* filterFacets

Using the information from the registry, we can build out our dashboard panels.
Panels use the high-level directive ``hzResourceTable`` that replaces common
templates so we do not need to write boilerplate HTML and controller code. It
gives developers a quick way to build a new table or change an existing table.

.. note::

    You may still choose to use the HTML template for complete control of form
    and functionality. For example, you may want to create a custom footer.
    You may also use the ``hzDynamicTable`` directive (what ``hzResourceTable``
    uses under the hood) directly. However, neither of these is extensible.
    You would need to override the panel completely.

This is a sample module file to demonstrate how to make some customizations to
the Images Panel.::

    (function() {
      'use strict';

      angular
        .module('horizon.app.core.images')
        .run(customizeImagePanel);

      customizeImagePanel.$inject = [
        'horizon.framework.conf.resource-type-registry.service',
        'horizon.app.core.images.basePath',
        'horizon.app.core.images.resourceType',
        'horizon.app.core.images.actions.surprise.service'
      ];

      function customizeImagePanel(registry, basePath, imageResourceType, surpriseService) {
        // get registry for ``OS::Glance::Image``
        registry = registry.getResourceType(imageResourceType);

        // replace existing Size column to make the font color red
        var column = {
          id: 'size',
          priority: 2,
          template: '<a style="color:red;">{$ item.size | bytes $}</a>'
        };
        registry.tableColumns.replace('size', column);

        // add a new detail view
        registry.detailsViews
          .append({
            id: 'anotherDetailView',
            name: gettext('Another Detail View'),
            template: basePath + 'demo/detail.html'
        });

        // set a different summary drawer template
        registry.setSummaryTemplateUrl(basePath + 'demo/drawer.html');

        // add a new global action
        registry.globalActions
          .append({
            id: 'surpriseAction',
            service: surpriseService,
            template: {
              text: gettext('Surprise')
            }
        });
      }
    })();

Additionally, you should have content defined in ``detail.html`` and
``drawer.html``, as well as define the ``surpriseService`` which is based off
the ``actions`` directive and needs allowed and perform methods defined.


Icons
=====

Horizon uses font icons from Font Awesome.  Please see `Font Awesome`_ for
instructions on how to use icons in the code.

To add icon to Table Action, use icon property. Example:

    class CreateSnapshot(tables.LinkAction):
       name = "snapshot"
       verbose_name = _("Create Snapshot")
       icon = "camera"

Additionally, the site-wide default button classes can be configured by
setting ``ACTION_CSS_CLASSES`` to a tuple of the classes you wish to appear
on all action buttons in your ``local_settings.py`` file.


Custom Stylesheets
==================

It is possible to define custom stylesheets for your dashboards. Horizon's base
template ``openstack_dashboard/templates/base.html`` defines multiple blocks
that can be overridden.

To define custom css files that apply only to a specific dashboard, create
a base template in your dashboard's templates folder, which extends Horizon's
base template e.g. ``openstack_dashboard/dashboards/my_custom_dashboard/
templates/my_custom_dashboard/base.html``.

In this template, redefine ``block css``. (Don't forget to include
``_stylesheets.html`` which includes all Horizon's default stylesheets.)::

    {% extends 'base.html' %}

    {% block css %}
      {% include "_stylesheets.html" %}

      {% load compress %}
      {% compress css %}
      <link href='{{ STATIC_URL }}my_custom_dashboard/scss/my_custom_dashboard.scss' type='text/scss' media='screen' rel='stylesheet' />
      {% endcompress %}
    {% endblock %}

The custom stylesheets then reside in the dashboard's own ``static`` folder
``openstack_dashboard/dashboards/my_custom_dashboard/static/
my_custom_dashboard/scss/my_custom_dashboard.scss``.

All dashboard's templates have to inherit from dashboard's base.html::

    {% extends 'my_custom_dashboard/base.html' %}
    ...


Custom Javascript
=================

Similarly to adding custom styling (see above), it is possible to include
custom javascript files.

All Horizon's javascript files are listed in the ``openstack_dashboard/
templates/horizon/_scripts.html`` partial template, which is included in
Horizon's base template in ``block js``.

To add custom javascript files, create an ``_scripts.html`` partial template in
your dashboard ``openstack_dashboard/dashboards/my_custom_dashboard/
templates/my_custom_dashboard/_scripts.html`` which extends
``horizon/_scripts.html``. In this template override the
``block custom_js_files`` including your custom javascript files::

    {% extends 'horizon/_scripts.html' %}

    {% block custom_js_files %}
        <script src='{{ STATIC_URL }}my_custom_dashboard/js/my_custom_js.js' type='text/javascript' charset='utf-8'></script>
    {% endblock %}


In your dashboard's own base template ``openstack_dashboard/dashboards/
my_custom_dashboard/templates/my_custom_dashboard/base.html`` override
``block js`` with inclusion of dashboard's own ``_scripts.html``::

    {% block js %}
        {% include "my_custom_dashboard/_scripts.html" %}
    {% endblock %}

The result is a single compressed js file consisting both Horizon and
dashboard's custom scripts.

Additionally, some marketing and analytics scripts require you to place them
within the page's <head> tag. To do this, place them within the
``horizon/_custom_head_js.html`` file. Similar to the ``_scripts.html`` file
mentioned above, you may link to an existing file::

    <script src='{{ STATIC_URL }}/my_custom_dashboard/js/my_marketing_js.js' type='text/javascript' charset='utf-8'></script>

or you can paste your script directly in the file, being sure to use
appropriate tags::

  <script type="text/javascript">
  //some javascript
  </script>


Customizing Meta Attributes
===========================

To add custom metadata attributes to your project's base template, include
them in the ``horizon/_custom_meta.html`` file. The contents of this file will
be inserted into the page's <head> just after the default Horizon meta tags.

..  _Font Awesome: https://fortawesome.github.io/Font-Awesome/