.. include:: ./substitutions.rst ============================ Customizing CKAN's templates ============================ CKAN pages are generated from Jinja2_ template files. This tutorial will walk you through the process of writing your own template files to modify and replace the default ones, and change the layout and content of CKAN pages. .. seealso:: :doc:`/contributing/string-i18n` How to mark strings for translation in your template files. ------------------------- Creating a CKAN extension ------------------------- A CKAN theme is simply a CKAN plugin that contains some custom templates and static files, so before getting started on our CKAN theme we'll have to create an extension and plugin. For a detailed explanation of the steps below, see :doc:`/extensions/tutorial`. 1. Use the ``ckan generate extension`` command as per the :doc:`/extensions/tutorial`. 2. Create the file |plugin.py| with the following contents: .. literalinclude:: /../ckanext/example_theme_docs/v01_empty_extension/plugin.py 3. Edit the ``entry_points`` in |setup.py| to look like this:: entry_points=''' [ckan.plugins] example_theme=ckanext.example_theme.plugin:ExampleThemePlugin ''', 4. Run ``python setup.py develop``: .. parsed-literal:: cd |extension_dir| python setup.py develop 5. Add the plugin to the ``ckan.plugins`` setting in your |ckan.ini| file:: ckan.plugins = stats text_view datatables_view example_theme 6. Start CKAN in the development web server: .. parsed-literal:: $ ckan -c |ckan.ini| run Starting server in PID 13961. serving on 0.0.0.0:5000 view at http://127.0.0.1:5000 Open the `CKAN front page`_ in your web browser. If your plugin is in the :ref:`ckan.plugins` setting and CKAN starts without crashing, then your plugin is installed and CKAN can find it. Of course, your plugin doesn't *do* anything yet. .. _template overriding: --------------------------------- Replacing a default template file --------------------------------- Every CKAN page is generated by rendering a particular template. For each page of a CKAN site there's a corresponding template file. For example the front page is generated from the `ckan/templates/home/index.html `_ file, the ``/about`` page is generated from `ckan/templates/home/about.html `_, the datasets page at ``/dataset`` is generated from `ckan/templates/package/search.html `_, etc. To customize pages, our plugin needs to register its own custom template directory containing template files that override the default ones. Edit the |plugin.py| file that we created earlier, so that it looks like this: .. literalinclude:: /../ckanext/example_theme_docs/v02_empty_template/plugin.py This new code does a few things: 1. It imports CKAN's :doc:`plugins toolkit ` module: .. literalinclude:: /../ckanext/example_theme_docs/v02_empty_template/plugin.py :start-after: import ckan.plugins as plugins :end-before: class ExampleThemePlugin(plugins.SingletonPlugin): The plugins toolkit is a Python module containing core functions, classes and exceptions for CKAN plugins to use. For more about the plugins toolkit, see :doc:`/extensions/tutorial`. 2. It calls :py:func:`~ckan.plugins.implements` to declare that it implements the :py:class:`~ckan.plugins.interfaces.IConfigurer` plugin interface: .. literalinclude:: /../ckanext/example_theme_docs/v02_empty_template/plugin.py :start-after: # Declare that this class implements IConfigurer. :end-before: def update_config( This tells CKAN that our :py:class:`~ckanext.example_theme.v2.plugin.ExampleThemePlugin` class implements the methods declared in the :py:class:`~ckan.plugins.interfaces.IConfigurer` interface. CKAN will call these methods of our plugin class at the appropriate times. 3. It implements the :py:meth:`~ckan.plugins.interfaces.IConfigurer.update_config` method, which is the only method declared in the :py:class:`~ckan.plugins.interfaces.IConfigurer` interface: .. literalinclude:: /../ckanext/example_theme_docs/v02_empty_template/plugin.py :pyobject: ExampleThemePlugin.update_config CKAN will call this method when it starts up, to give our plugin a chance to modify CKAN's configuration settings. Our :py:meth:`~ckanext.example_theme.v2.plugin.ExampleThemePlugin.update_config` method calls :py:func:`~ckan.plugins.toolkit.add_template_directory` to register its custom template directory with CKAN. This tells CKAN to look for template files in |templates_dir| whenever it renders a page. Any template file in this directory that has the same name as one of CKAN's default template files, will be used instead of the default file. Now, let's customize the CKAN front page. We first need to discover which template file CKAN uses to render the front page, so we can replace it. Set :ref:`debug` to ``true`` in your |ckan.ini| file:: [DEFAULT] # WARNING: *THIS SETTING MUST BE SET TO FALSE ON A PRODUCTION ENVIRONMENT* debug = true .. _debug footer: Reload the `CKAN front page`_ in your browser, and you should see a *Debug* link in the footer at the bottom of the page. Click on this link to open the debug footer. The debug footer displays various information useful for CKAN frontend development and debugging, including the names of the template files that were used to render the current page: .. image:: /images/debug-footer.png :alt: The debug footer. The first template file listed is the one we're interested in:: Template name: home/index.html Template path: /usr/lib/ckan/default/src/ckan/ckan/templates/home/index.html This tells us that ``home/index.html`` is the root template file used to render the front page. The debug footer appears at the bottom of every CKAN page, and can always be used to find the page's template files, and other information about the page. .. note:: Most CKAN pages are rendered from multiple template files. The first file listed in the debug footer is the root template file of the page. All other template files used to render the page (listed further down in the debug footer) are either included by the root file, or included by another file that is included by the root file. To figure out which template file renders a particular part of the page you have to inspect the `source code of the template files `_, starting with the root file. Now let's override ``home/index.html`` using our plugins' custom ``templates`` directory. Create the |templates_dir| directory, create a ``home`` directory inside the ``templates`` directory, and create an empty ``index.html`` file inside the ``home`` directory: .. parsed-literal:: |extension_dir|/ ckanext/ example_theme/ templates/ home/ index.html <-- An empty file. If you now restart the development web server (kill the server using Ctrl-c, then run the ``ckan run`` command again) and reload the `CKAN front page`_ in your web browser, you should see an empty page, because we've replaced the template file for the front page with an empty file. .. note:: If you run ``ckan run`` without the ``-r(--disable-reloader)`` option, then it isn't usually necessary to restart the server after editing a Python file, a template file, your CKAN config file, or any other CKAN file. If you've added a new file or directory, however, you need to restart the server manually. ------ Jinja2 ------ CKAN template files are written in the `Jinja2`_ templating language. Jinja template files, such as our ``index.html`` file, are simply text files that, when processed, generate any text-based output format such as ``HTML``, ``XML``, ``CSV``, etc. Most of the template files in CKAN generate ``HTML``. We'll introduce some Jinja2 basics below. Jinja2 templates have many more features than these, for full details see the `Jinja2 docs`_. .. _expressions and variables: Expressions and variables ========================= Jinja2 *expressions* are snippets of code between ``{{ ... }}`` delimiters, when a template is rendered any expressions are evaluated and replaced with the resulting value. The simplest use of an expression is to display the value of a variable, for example ``{{ foo }}`` in a template file will be replaced with the value of the variable ``foo`` when the template is rendered. CKAN makes a number of global variables available to all templates. One such variable is :py:data:`app_globals`, which can be used to access certain global attributes including some of the settings from your CKAN config file. For example, to display the value of the :ref:`ckan.site_title` setting from your config file you would put this code in any template file: .. literalinclude:: /../ckanext/example_theme_docs/v03_jinja/templates/home/index.html :language: django :start-after: Jinja variable example :end-before: End example .. note:: The :py:data:`app_globals` variable is also sometimes called :py:data:`g` (an alias), you may see :py:data:`g` in some CKAN templates. See :doc:`variables-and-functions`. .. note:: Not all config settings are available to templates via :py:data:`app_globals`. The :ref:`sqlalchemy.url` setting, for example, contains your database password, so making that variable available to templates might be a security risk. If you've added your own custom options to your config file, these will not be available in :py:data:`app_globals` automatically. See :ref:`accessing custom config settings from templates`. .. note:: If a template tries to render a variable or attribute that doesn't exist, rather than crashing or giving an error message, the Jinja2 expression simply evaluates to nothing (an empty string). For example, these Jinja2 expressions will output nothing:: {{ app_globals.an_attribute_that_does_not_exist }} {{ a_variable_that_does_not_exist }} If, on the other hand, you try to render an attribute of a variable that doesn't exist, then Jinja2 will crash. For example, this Jinja2 expression will crash with an ``UndefinedError: 'a_variable_that_does_not_exist' is undefined``:: {{ a_variable_that_does_not_exist.an_attribute_that_does_not_exist }} See the `Jinja2 variables docs `_ for details. .. note:: Jinja2 expressions can do much more than print out the values of variables, for example they can call Jinja2's `global functions `_, CKAN's :ref:`template helper functions