In the previous part, we developed a CKAN plugin together. In this part, we want to create a user interface or as CKAN calls it “templates”. If you did not read the first part, I suggest having a look since we are using the plugin source code from it:
These are the main sections of this writing episode:
- Create a template for our plugin function
- Create snippets
- Inherit from the CKAN template
- Customize an existing CKAN template
- Define web assets for our plugin
- Define a template Helper function
Again it is worth mentioning that most of these topics are covered by the CKAN documentation. The goal here is to look at them more in detail with an example code and understand it better. Also, this helps to have all template-related topics in one place.
https://docs.ckan.org/en/2.9/theming/index.html
The source code for this writing series is available on my GitHub:
https://github.com/Pooya-Oladazimi/ckanext-my-first-cool-extension
Create a template for our plugin
So far, our plugin function do_something only returns a string. However, often this is not the case and we need an actual user interface. CKAN user interface files are called Templates. We saw together how can we define the root templates directory for our plugin. Now, let us define our first template and call it index.html

Note: remember that our root path for templates is templates/cool_temp as we configured in the plugin.py
Let’s add some content to our new template:

We also create a new plugin function with a defined URL route (look at the first part of this series to see how to do it in CKAN). We call the function show_something.
The function utilizes the Flask render_template to return the template file to the user:

Now if you open http://MY_DOMAIN/cool_plugin/show_something you will the message!
Render template with value
Now let’s assume that we want to show a list of objects in our template. To do this, we need to pass the list with a defined name to our plugin. Then inside the template, we use that template variable to render its values. Our template variable is “names” and we use a Jinja2 for-loop to show its values.
def show_something():
objects = ["Car", "Ship", "Plane"]
return render_template("index.html", names=objects)
Template:

Now if you open the plugin function URL, you see this:

Snippets: reusable views
Snippets are reusable template codes. In CKAN, we use snippets for two main purposes:
- Code re-usability
- Code separation
Let’s consider our template code. The row and col-sm-12 structure is repetitive inside the for-loop and outside it. Besides, this structure is a very common one in developing a user interface. So, let’s put it in a snippet. This way we can re-use it whenever we want.
To define a snippet, first, we need to create a new directory inside our template root directory named snippets. Then we create our snippet file there.

Important Note: It is strongly recommended to put your plugin name at the beginning of your snippet file name. This way you make sure there will not be any name conflict since plugin names are unique in CKAN. (Other plugins and CKAN itself also contain snippets).
Snippets code (cool_plugin_simple_struc.html) :

The input is the variable that the snippet expects to receive when you call it inside your template
The template:

We use the keyword snippet to call our snippet view.
Inherit from the CKAN template
CKAN allows you to inherit from its core templates for your plugin. One of the scenarios for using this feature is when you want to wrap your plugin content with CKAN layouts. For example, let’s say we want to add the CKAN site header and footer to our plugin list of objects.
To inherit, first, we need to find the target template in CKAN. In our example, we need to inherit from the page.html in CKAN. This is the template that creates the site header, main content, and footer. To inherit a template, we need to use the extends keyword in Jinja2.
The last step is to find the target block in the target template. Jinja2 allows us to use blocks in our template code that we can overwrite in other templates. To do this, we need to define a block in our plugin template with exact same name as the parent block to overwrite it.

- The block secondary is responsible for the left side content on a CKAN page. We make it empty since we do not want the left panel content in our plugin.
- The block primary is responsible for the main content on a CKAN page
Now if you open your plugin page, you see the list of objects with the site header and footer!
To understand the content of page.html better, feel free to look at it in “/usr/lib/ckan/default/src/ckan/ckan/templates/page.html”
Customize an existing CKAN template
In CKAN, we can also customize an existing CKAN template and view. To do this, let’s define a scenario. Let’s say we want to add a new button on a dataset page in CKAN under its title and above its description:

The way we do this is very similar to inheriting steps. Here also need extends and blocks. However, here we need to do one extra step at the beginning. We need to find the target template and create the exact same one in our plugin.
In this example, the target CKAN template is read.html which you can find here: “/usr/lib/ckan/default/src/ckan/ckan/templates/package/read.html”
We create the exact same file with the exact same path as the CKAN template directory in our plugin. This way CKAN knows it needs to use our template instead of the parent.

The rest is the same as the inheriting except one little different. Here, we use the super() function to tell CKAN that we want to add the content to the existing content. It means keeping the parent content and adding my content to it. Also, instead of extends, we use ckan_extends on the top of the template.

After this, you can see the new button when opening a dataset page in CKAN:

Hints:
- Always put ckan_extends on top
- You may need to restart CKAN if you add a new template to customize the site.
- You can find all CKAN core templates here: “/usr/lib/ckan/default/src/ckan/ckan/templates”
- The same concepts also exist for CKAN core snippets (you can overwrite them)
- Remember to use the exact same path as the target file in CKAN. The path inside the CKAN template directory.
Define web assets for our plugin
Currently, our plugin does not have any CSS and JS. Often we need these to style our pages and to do client-side calculations such as sending Ajax requests. In CKAN we need to define a web asset YAML file to introduce our static assets. This is especially important for JS files since we have to have a YAML to serve them on the server.
In the plugin, we want to do these things. Our target is the “New button!” that we added in the previous step:
- Change the “New button!” background color to green
- Alert a message when a user clicks on the button
The first step is that we define CSS and JS files to do the above tasks. We need to define our static files in the plugin root public directory as we configured before. Besides, we define the webassets.yml



The next step is to config our plugin and set the web asset name. We utilize the add_resource function in the CKAN toolkit. We name our asset ckanext-cool-plugin. Note that the asset name can be anything but you need to make sure it is unique for your plugin.

Then, we add content to our YAML file. We need to define two asset entries. Each for one of the CSS and JS files.

Points:
- The names cool-css and cool-js are custom and can be anything
- You do not need to change %(version)s_my.css and %(version)s_my.css
- contents including the actual target files. You can use multiple files at once.
- You can use preload to load the needed JS libraries. Here we only need JQuery. The vendor exists in “/usr/lib/ckan/default/src/ckan/ckan/public/base/vendor”
At last, we load our new assets into the template that we used to add the “new button!”

Remember to put the asset in the block if you are overwriting a parent template in CKAN.
Note: you may need to restart ckan to see the changes.
Outcome:


Define a template Helper function
The last topic about CKAN templates is the Helper functions. These functions are the ones we use to do some computing while rendering a template. CKAN already has some of them: https://docs.ckan.org/en/2.9/theming/template-helper-functions.html
We can also define our custom helper function in our plugin. First, we need to implement the ITemplateHelpers interface from CKAN:

Second, we implement the get_helpers function from the interface to add our new helper. The helper name will be “help_me”. We also define a new function for the helper. Our new function help_it takes a number and returns a random number that is greater than it.


Finally, we use this helper function in our template to add a random number greater than 100 to our “new button” text. To use the helper function, we utilize the h helper toolkit in CKAN:

Outcome:

Important Note: The helper function is global. It means all other plugins can use it. So be careful while picking a name for your helper!
The End.
3 thoughts on “How to develop a CKAN plugin: Part 2, Templates”