Introduction

A CKAN extension is a Python package that modifies or extends CKAN. Each extension contains one or more plugins that must be added to your CKAN config file to activate the extension’s features.

Creating and Installing extensions

  1. Boot up your docker compose
docker-compose -f docker-compose.dev.yml up
  1. To create an extension template using this docker composition execute:
docker-compose -f docker-compose.dev.yml exec ckan-dev /bin/bash -c "paster --plugin=ckan create -t ckanext ckanext-example_extension -o /srv/app/src_extensions"

This command will create an extension template in your local ./src folder that is mounted inside the containers in the /srv/app/src_extension directory. Any extension cloned on the src folder will be installed in the CKAN container when booting up Docker Compose (docker-compose up). This includes installing any requirements listed in a requirements.txt (or pip-requirements.txt) file and running python setup.py develop.

  1. Add the plugin to the CKAN__PLUGINS setting in your .env file.
CKAN__PLUGINS=stats text_view recline_view example_extension
  1. Restart your docker-compose:
# Shut down your instance with crtl+c and then run it again with:
docker-compose -f docker-compose.dev.yml up

CKAN will be started running on the paster development server with the '–reload' option to watch changes in the extension files.

You should see the following output in the console:

...
ckan-dev_1    | Installed /srv/app/src_extensions/ckanext-example_extension
...

Edit the extension

Let's edit a template to change the way CKAN is displayed to the user!

  1. First you will need write permissions to the extension folder since it was created by the user running docker. Replace your_username and execute the following command:

You can find out your current username by typing 'echo $USER' in the terminal.

sudo chown -R <your_username>:<your_username> src/ckanext-example_extension
  1. The previous comamand creates all the files and folder structure needed for our extension. Open src/ckanext-example_extension/ckanext/example_extension/plugin.py to see the main file of our extension that we will edit to add custom functionality:
import ckan.plugins as plugins
import ckan.plugins.toolkit as toolkit


class Example_ExtensionPlugin(plugins.SingletonPlugin):
    plugins.implements(plugins.IConfigurer)

    # IConfigurer

    def update_config(self, config_):
        toolkit.add_template_directory(config_, 'templates')
        toolkit.add_public_directory(config_, 'public')
        toolkit.add_resource('fanstatic', 'example_theme')
  1. We will create a custom Flask Blueprint to extend our CKAN instance with more endpoints. In order to create a new blueprint and add an endpoint we need to:
  • Import Blueprint and render_template from the flask module.
  • Create the functions that will be used as endpoints
  • Implement the IBlueprint interface in our plugin and add the new endpoint.
  1. From flask import Blueprint and render_template,
import ckan.plugins as plugins
import ckan.plugins.toolkit as toolkit

from flask import Blueprint, render_template

class Example_ExtensionPlugin(plugins.SingletonPlugin):
    plugins.implements(plugins.IConfigurer)

    # IConfigurer

    def update_config(self, config_):
        toolkit.add_template_directory(config_, 'templates')
        toolkit.add_public_directory(config_, 'public')
        toolkit.add_resource('fanstatic', 'example_extension')
  1. Create a new function: hello_plugin
import ckan.plugins as plugins
import ckan.plugins.toolkit as toolkit

from flask import Blueprint, render_template

def hello_plugin():
    u'''A simple view function'''
    return u'Hello World, this is served from an extension'

class Example_ExtensionPlugin(plugins.SingletonPlugin):
    plugins.implements(plugins.IConfigurer)

    # IConfigurer

    def update_config(self, config_):
        toolkit.add_template_directory(config_, 'templates')
        toolkit.add_public_directory(config_, 'public')
        toolkit.add_resource('fanstatic', 'example_extension')
  1. Implement the IBlueprint interface in our plugin and add the new endpoint.
import ckan.plugins as plugins
import ckan.plugins.toolkit as toolkit

from flask import Blueprint, render_template

def hello_plugin():
    u'''A simple view function'''
    return u'Hello World, this is served from an extension'

class Example_ExtensionPlugin(plugins.SingletonPlugin):
    plugins.implements(plugins.IConfigurer)
    plugins.implements(plugins.IBlueprint)

    # IConfigurer

    def update_config(self, config_):
        toolkit.add_template_directory(config_, 'templates')
        toolkit.add_public_directory(config_, 'public')
        toolkit.add_resource('fanstatic', 'example_extension')

    # IBlueprint

    def get_blueprint(self):
        u'''Return a Flask Blueprint object to be registered by the app.'''
        # Create Blueprint for plugin
        blueprint = Blueprint(self.name, self.__module__)
        blueprint.template_folder = u'templates'
        # Add plugin url rules to Blueprint object
        blueprint.add_url_rule('/hello_plugin', '/hello_plugin', hello_plugin)
        return blueprint

  1. Go back to the browser and navigate to http://ckan:5000/hello_plugin. You should see the value returned by our view!

New Blueprint output

Now that you have added a new view and endpoint to your plugin you are ready for the next step of the tutorial! You can also check the complete code of this plugin in the ckan repository.