Plugin interfaces reference

ckan.plugins contains a few core classes and functions for plugins to use:

ckan.plugins

class ckan.plugins.SingletonPlugin(*args, **kwargs)

Base class for plugins which are singletons (ie most of them)

One singleton instance of this class will be created when the plugin is loaded. Subsequent calls to the class constructor will always return the same singleton instance.

class ckan.plugins.Plugin(*args, **kwargs)

Base class for plugins which require multiple instances.

Unless you need multiple instances of your plugin object you should probably use SingletonPlugin.

ckan.plugins.implements(interface, inherit=None, namespace=None, service=False)

Can be used in the class definition of Plugin subclasses to declare the extension points that are implemented by this interface class.

ckan.plugins.interfaces

A collection of interfaces that CKAN plugins can implement to customize and extend CKAN.

class ckan.plugins.interfaces.Interface

Base class for custom interfaces.

Marker base class for extension point interfaces. This class is not intended to be instantiated. Instead, the declaration of subclasses of Interface are recorded, and these classes are used to define extension points.

classmethod provided_by(instance: SingletonPlugin) bool

Check that the object is an instance of the class that implements the interface.

classmethod implemented_by(other: Type['SingletonPlugin']) bool

Check whether the class implements the current interface.

class ckan.plugins.interfaces.IMiddleware

Hook into the CKAN middleware stack

Note that methods on this interface will be called two times, one for the Pylons stack and one for the Flask stack (eventually there will be only the Flask stack).

make_middleware(app: CKANApp, config: CKANConfig) CKANApp

Return an app configured with this middleware

When called on the Flask stack, this method will get the actual Flask app so plugins wanting to install Flask extensions can do it like this:

import ckan.plugins as p
from flask_mail import Mail

class MyPlugin(p.SingletonPlugin):

    p.implements(p.IMiddleware)

    def make_middleware(app, config):

        mail = Mail(app)

        return app
make_error_log_middleware(app: CKANFlask, config: CKANConfig) CKANFlask

Return an app configured with this error log middleware

Note that both on the Flask and Pylons middleware stacks, this method will receive a wrapped WSGI app, not the actual Flask or Pylons app.

class ckan.plugins.interfaces.IAuthFunctions

Override CKAN’s authorization functions, or add new auth functions.

get_auth_functions() dict[str, AuthFunction]

Return the authorization functions provided by this plugin.

Return a dictionary mapping authorization function names (strings) to functions. For example:

{'user_create': my_custom_user_create_function,
 'group_create': my_custom_group_create}

When a user tries to carry out an action via the CKAN API or web interface and CKAN or a CKAN plugin calls check_access('some_action') as a result, an authorization function named 'some_action' will be searched for in the authorization functions registered by plugins and in CKAN’s core authorization functions (found in ckan/logic/auth/).

For example when action function 'package_create' is called, a 'package_create' authorization function is searched for.

If an extension registers an authorization function with the same name as one of CKAN’s default authorization functions (as with 'user_create' and 'group_create' above), the extension’s function will override the default one.

Each authorization function should take two parameters context and data_dict, and should return a dictionary {'success': True} to authorize the action or {'success': False} to deny it, for example:

def user_create(context, data_dict=None):
    if (some condition):
        return {'success': True}
    else:
        return {'success': False, 'msg': 'Not allowed to register'}

The context object will contain a model that can be used to query the database, a user containing the name of the user doing the request (or their IP if it is an anonymous web request) and an auth_user_obj containing the actual model.User object (or None if it is an anonymous request).

See ckan/logic/auth/ for more examples.

Note that by default, all auth functions provided by extensions are assumed to require a validated user or API key, otherwise a ckan.logic.NotAuthorized: exception will be raised. This check will be performed before calling the actual auth function. If you want to allow anonymous access to one of your actions, its auth function must be decorated with the auth_allow_anonymous_access decorator, available in the plugins toolkit.

For example:

import ckan.plugins as p

@p.toolkit.auth_allow_anonymous_access
def my_search_action(context, data_dict):
    # Note that you can still return {'success': False} if for some
    # reason access is denied.

def my_create_action(context, data_dict):
    # Unless there is a logged in user or a valid API key provided
    # NotAuthorized will be raised before reaching this function.

By decorating a registered auth function with the ckan.plugins.toolkit.chained_auth_function decorator you can create a chain of auth checks that are completed when auth is requested. This chain starts with the last chained auth function to be registered and ends with the original auth function (or a non-chained plugin override version). Chained auth functions must accept an extra parameter, specifically the next auth function in the chain, for example:

auth_function(next_auth, context, data_dict).

The chained auth function may call the next_auth function, optionally passing different values, handling exceptions, returning different values and/or raising different exceptions to the caller.

class ckan.plugins.interfaces.IDomainObjectModification

Receives notification of new, changed and deleted datasets.

notify(entity: Any, operation: str) None

Send a notification on entity modification.

Parameters:
  • entity – instance of module.Package.

  • operation – ‘new’, ‘changed’ or ‘deleted’.

notify_after_commit(entity: Any, operation: Any) None

** DEPRECATED **

Supposed to send a notification after entity modification, but it doesn’t work.

Parameters:
  • entity – instance of module.Package.

  • operation – ‘new’, ‘changed’ or ‘deleted’.

class ckan.plugins.interfaces.IFeed

For extending the default Atom feeds

get_feed_class() PFeedFactory

Allows plugins to provide a custom class to generate feed items.

Returns:

feed class

Return type:

type

The feed item generator’s constructor is called as follows:

feed_class(
    feed_title,        # Mandatory
    feed_link,         # Mandatory
    feed_description,  # Mandatory
    language,          # Optional, always set to 'en'
    author_name,       # Optional
    author_link,       # Optional
    feed_guid,         # Optional
    feed_url,          # Optional
    previous_page,     # Optional, url of previous page of feed
    next_page,         # Optional, url of next page of feed
    first_page,        # Optional, url of first page of feed
    last_page,         # Optional, url of last page of feed
)
get_item_additional_fields(dataset_dict: dict[str, Any]) dict[str, Any]

Allows plugins to set additional fields on a feed item.

Parameters:

dataset_dict (dictionary) – the dataset metadata

Returns:

the fields to set

Return type:

dictionary

class ckan.plugins.interfaces.IGroupController

Hook into the Group view. These methods will usually be called just before committing or returning the respective object i.e. when all validation, synchronization and authorization setup are complete.

read(entity: model.Group) None

Called after IGroupController.before_view inside group_read.

create(entity: model.Group) None

Called after group has been created inside group_create.

edit(entity: model.Group) None

Called after group has been updated inside group_update.

delete(entity: model.Group) None

Called before commit inside group_delete.

before_view(data_dict: dict[str, Any]) dict[str, Any]

Extensions will receive this before the group gets displayed. The dictionary passed will be the one that gets sent to the template.

class ckan.plugins.interfaces.IOrganizationController

Hook into the Organization view. These methods will usually be called just before committing or returning the respective object i.e. when all validation, synchronization and authorization setup are complete.

read(entity: model.Group) None

Called after IOrganizationController.before_view inside organization_read.

create(entity: model.Group) None

Called after organization had been created inside organization_create.

edit(entity: model.Group) None

Called after organization had been updated inside organization_update.

delete(entity: model.Group) None

Called before commit inside organization_delete.

before_view(data_dict: dict[str, Any]) dict[str, Any]

Extensions will receive this before the organization gets displayed. The dictionary passed will be the one that gets sent to the template.

class ckan.plugins.interfaces.IPackageController

Hook into the dataset view.

read(entity: model.Package) None

Called after IPackageController.before_dataset_view inside package_show.

create(entity: model.Package) None

Called after the dataset had been created inside package_create.

edit(entity: model.Package) None

Called after the dataset had been updated inside package_update.

delete(entity: model.Package) None

Called before commit inside package_delete.

after_dataset_create(context: Context, pkg_dict: dict[str, Any]) None

Extensions will receive the validated data dict after the dataset has been created (Note that the create method will return a dataset domain object, which may not include all fields). Also the newly created dataset id will be added to the dict.

after_dataset_update(context: Context, pkg_dict: dict[str, Any]) None

Extensions will receive the validated data dict after the dataset has been updated.

Note that bulk dataset update actions (bulk_update_private, bulk_update_public) will bypass this callback. See ckan.plugins.toolkit.chained_action to wrap those actions if required.

after_dataset_delete(context: Context, pkg_dict: dict[str, Any]) None

Extensions will receive the data dict (typically containing just the dataset id) after the dataset has been deleted.

Note that the bulk_update_delete action will bypass this callback. See ckan.plugins.toolkit.chained_action to wrap that action if required.

after_dataset_show(context: Context, pkg_dict: dict[str, Any]) None

Extensions will receive the validated data dict after the dataset is ready for display.

Extensions will receive a dictionary with the query parameters, and should return a modified (or not) version of it.

search_params will include an extras dictionary with all values from fields starting with ext_, so extensions can receive user input from specific fields.

Extensions will receive the search results, as well as the search parameters, and should return a modified (or not) object with the same structure:

{'count': '', 'results': '', 'search_facets': ''}

Note that count and facets may need to be adjusted if the extension changed the results for some reason.

search_params will include an extras dictionary with all values from fields starting with ext_, so extensions can receive user input from specific fields.

before_dataset_index(pkg_dict: dict[str, Any]) dict[str, Any]

Extensions will receive what will be given to Solr for indexing. This is essentially a flattened dict (except for multi-valued fields such as tags) of all the terms sent to the indexer. The extension can modify this by returning an altered version.

before_dataset_view(pkg_dict: dict[str, Any]) dict[str, Any]

Extensions will receive this before the dataset gets displayed. The dictionary passed will be the one that gets sent to the template.

class ckan.plugins.interfaces.IPluginObserver

Hook into the plugin loading mechanism itself

before_load(plugin: SingletonPlugin) None

Called before a plugin is loaded. This method is passed the plugin class.

after_load(service: Any) None

Called after a plugin has been loaded. This method is passed the instantiated service object.

before_unload(plugin: SingletonPlugin) None

Called before a plugin is loaded. This method is passed the plugin class.

after_unload(service: Any) None

Called after a plugin has been unloaded. This method is passed the instantiated service object.

class ckan.plugins.interfaces.IConfigurable

Hook called during the startup of CKAN

See also IConfigurer.

configure(config: CKANConfig) None

Called during CKAN’s initialization.

This function allows plugins to initialize themselves during CKAN’s initialization. It is called after most of the environment (e.g. the database) is already set up.

Note that this function is not only called during the initialization of the main CKAN process but also during the execution of paster commands and background jobs, since these run in separate processes and are therefore initialized independently.

Parameters:

config (ckan.common.CKANConfig) – dict-like configuration object

class ckan.plugins.interfaces.IConfigDeclaration

Register additional configuration options.

While it’s not necessary, declared config options can be printed out using CLI or additionally verified in code. This makes the task of adding new configuration, removing obsolete config options, checking the sanity of config options much simpler for extension consumers.

declare_config_options(declaration: Declaration, key: Key)

Register extra config options.

Example:

from ckan.config.declaration import Declaration, Key

def declare_config_options(
    self, declaration: Declaration, key: Key):

    declaration.annotate("MyExt config section")
    group = key.ckanext.my_ext.feature
    declaration.declare(group.enabled, "no").set_description(
        "Enables feature"
    )
    declaration.declare(group.mode, "simple").set_description(
        "Execution mode"
    )

Run ckan config declaration my_ext --include-docs and get the following config suggestion:

## MyExt config section ######################
# Enables feature
ckanext.my_ext.feature.enabled = no
# Execution mode
ckanext.my_ext.feature.mode = simple

See declare configuration guide for details.

Parameters:
  • declaration (ckan.config.declaration.Declaration) – object containing all the config declarations

  • key (ckan.config.declaration.Key) – object for generic option access.

class ckan.plugins.interfaces.IConfigurer

Configure the CKAN environment via the config object

See also IConfigurable.

update_config(config: CKANConfig) None

Called by load_environment at the earliest point that config is available to plugins. The config should be updated in place.

Parameters:

configconfig object

update_config_schema(schema: Schema) Schema

Return a schema with the runtime-editable config options.

CKAN will use the returned schema to decide which configuration options can be edited during runtime (using ckan.logic.action.update.config_option_update()) and to validate them before storing them.

Defaults to ckan.logic.schema.default_update_configuration_schema(), which will be passed to all extensions implementing this method, which can add or remove runtime-editable config options to it.

Parameters:

schema (dictionary) – a dictionary mapping runtime-editable configuration option keys to lists of validator and converter functions to be applied to those keys

Returns:

a dictionary mapping runtime-editable configuration option keys to lists of validator and converter functions to be applied to those keys

Return type:

dictionary

class ckan.plugins.interfaces.IActions

Allow adding of actions to the logic layer.

get_actions() dict[str, Callable[[Context, dict[str, Any]], Any]]

Should return a dict, the keys being the name of the logic function and the values being the functions themselves.

By decorating a function with the ckan.logic.side_effect_free decorator, the associated action will be made available to a GET request (as well as the usual POST request) through the Action API.

By decorating a function with ckan.plugins.toolkit.chained_action, the action will ‘intercept’ calls to an existing action function. This allows a plugin to modify the behaviour of an existing action function. Chained actions must be defined as action_function(original_action, context, data_dict), where the function’s name matches the original action function it intercepts, the first parameter is the action function it intercepts (in the next plugin or in core ckan). The chained action may call the original_action function, optionally passing different values, handling exceptions, returning different values and/or raising different exceptions to the caller. When multiple plugins chain to an action, the first plugin declaring is called first, and if it chooses to call the original_action, then the chained action in the next plugin to be declared next is called, and so on.

class ckan.plugins.interfaces.IResourceUrlChange

Receives notification of changed URL on a resource.

notify(resource: model.Resource) None

Called when a resource url has changed.

:param resource, instance of model.Resource

class ckan.plugins.interfaces.IDatasetForm

Customize CKAN’s dataset (package) schemas and forms.

By implementing this interface plugins can customise CKAN’s dataset schema, for example to add new custom fields to datasets.

Multiple IDatasetForm plugins can be used at once, each plugin associating itself with different dataset types using the package_types() and is_fallback() methods below, and then providing different schemas and templates for different types of dataset. When a dataset view action is invoked, the type field of the dataset will determine which IDatasetForm plugin (if any) gets delegated to.

When implementing IDatasetForm, you can inherit from ckan.plugins.toolkit.DefaultDatasetForm, which provides default implementations for each of the methods defined in this interface.

See ckanext/example_idatasetform for an example plugin.

package_types() Sequence[str]

Return an iterable of dataset (package) types that this plugin handles.

If a request involving a dataset of one of the returned types is made, then this plugin instance will be delegated to.

There cannot be two IDatasetForm plugins that return the same dataset type, if this happens then CKAN will raise an exception at startup.

Return type:

iterable of strings

is_fallback() bool

Return True if this plugin is the fallback plugin.

When no IDatasetForm plugin’s package_types() match the type of the dataset being processed, the fallback plugin is delegated to instead.

There cannot be more than one IDatasetForm plugin whose is_fallback() method returns True, if this happens CKAN will raise an exception at startup.

If no IDatasetForm plugin’s is_fallback() method returns True, CKAN will use DefaultDatasetForm as the fallback.

Return type:

bool

create_package_schema() Schema

Return the schema for validating new dataset dicts.

CKAN will use the returned schema to validate and convert data coming from users (via the dataset form or API) when creating new datasets, before entering that data into the database.

If it inherits from ckan.plugins.toolkit.DefaultDatasetForm, a plugin can call DefaultDatasetForm’s create_package_schema() method to get the default schema and then modify and return it.

CKAN’s convert_to_tags() or convert_to_extras() functions can be used to convert custom fields into dataset tags or extras for storing in the database.

See ckanext/example_idatasetform for examples.

Returns:

a dictionary mapping dataset dict keys to lists of validator and converter functions to be applied to those keys

Return type:

dictionary

update_package_schema() Schema

Return the schema for validating updated dataset dicts.

CKAN will use the returned schema to validate and convert data coming from users (via the dataset form or API) when updating datasets, before entering that data into the database.

If it inherits from ckan.plugins.toolkit.DefaultDatasetForm, a plugin can call DefaultDatasetForm’s update_package_schema() method to get the default schema and then modify and return it.

CKAN’s convert_to_tags() or convert_to_extras() functions can be used to convert custom fields into dataset tags or extras for storing in the database.

See ckanext/example_idatasetform for examples.

Returns:

a dictionary mapping dataset dict keys to lists of validator and converter functions to be applied to those keys

Return type:

dictionary

show_package_schema() Schema

Return a schema to validate datasets before they’re shown to the user.

CKAN will use the returned schema to validate and convert data coming from the database before it is returned to the user via the API or passed to a template for rendering.

If it inherits from ckan.plugins.toolkit.DefaultDatasetForm, a plugin can call DefaultDatasetForm’s show_package_schema() method to get the default schema and then modify and return it.

If you have used convert_to_tags() or convert_to_extras() in your create_package_schema() and update_package_schema() then you should use convert_from_tags() or convert_from_extras() in your show_package_schema() to convert the tags or extras in the database back into your custom dataset fields.

See ckanext/example_idatasetform for examples.

Returns:

a dictionary mapping dataset dict keys to lists of validator and converter functions to be applied to those keys

Return type:

dictionary

setup_template_variables(context: Context, data_dict: dict[str, Any]) None

Add variables to the template context for use in dataset templates.

This function is called before a dataset template is rendered. If you have custom dataset templates that require some additional variables, you can add them to the template context ckan.plugins.toolkit.c here and they will be available in your templates. See ckanext/example_idatasetform for an example.

new_template(package_type: str) str

Return the path to the template for the new dataset page.

The path should be relative to the plugin’s templates dir, e.g. 'package/new.html'.

Return type:

string

read_template(package_type: str) str

Return the path to the template for the dataset read page.

The path should be relative to the plugin’s templates dir, e.g. 'package/read.html'.

If the user requests the dataset in a format other than HTML, then CKAN will try to render a template file with the same path as returned by this function, but a different filename extension, e.g. 'package/read.rdf'. If your extension (or another one) does not provide this version of the template file, the user will get a 404 error.

Return type:

string

edit_template(package_type: str) str

Return the path to the template for the dataset edit page.

The path should be relative to the plugin’s templates dir, e.g. 'package/edit.html'.

Return type:

string

search_template(package_type: str) str

Return the path to the template for use in the dataset search page.

This template is used to render each dataset that is listed in the search results on the dataset search page.

The path should be relative to the plugin’s templates dir, e.g. 'package/search.html'.

Return type:

string

history_template(package_type: str) str

Warning

This template is removed. The function exists for compatibility. It now returns None.

resource_template(package_type: str) str

Return the path to the template for the resource read page.

The path should be relative to the plugin’s templates dir, e.g. 'package/resource_read.html'.

Return type:

string

package_form(package_type: str) str

Return the path to the template for the dataset form.

The path should be relative to the plugin’s templates dir, e.g. 'package/form.html'.

Return type:

string

resource_form(package_type: str) str

Return the path to the template for the resource form.

The path should be relative to the plugin’s templates dir, e.g. 'package/snippets/resource_form.html'

Return type:

string

validate(context: Context, data_dict: DataDict, schema: Schema, action: str) tuple[dict[str, Any], dict[str, Any]] | None

Customize validation of datasets.

When this method is implemented it is used to perform all validation for these datasets. The default implementation calls and returns the result from ckan.plugins.toolkit.navl_validate.

This is an adavanced interface. Most changes to validation should be accomplished by customizing the schemas returned from show_package_schema(), create_package_schema() and update_package_schema(). If you need to have a different schema depending on the user or value of any field stored in the dataset, or if you wish to use a different method for validation, then this method may be used.

Parameters:
  • context (dictionary) – extra information about the request

  • data_dict (dictionary) – the dataset to be validated

  • schema (dictionary) – a schema, typically from show_package_schema(), create_package_schema() or update_package_schema()

  • action (string) – 'package_show', 'package_create' or 'package_update'

Returns:

(data_dict, errors) where data_dict is the possibly-modified dataset and errors is a dictionary with keys matching data_dict and lists-of-string-error-messages as values

Return type:

(dictionary, dictionary)

prepare_dataset_blueprint(package_type: str, blueprint: Blueprint) Blueprint

Update or replace dataset blueprint for given package type.

Internally CKAN registers blueprint for every custom dataset type. Before default routes added to this blueprint and it registered inside application this method is called. It can be used either for registration of the view function under new path or under existing path(like /new), in which case this new function will be used instead of default one.

Note, this blueprint has prefix /{package_type}.

Return type:

flask.Blueprint

prepare_resource_blueprint(package_type: str, blueprint: Blueprint) Blueprint

Update or replace resource blueprint for given package type.

Internally CKAN registers separate resource blueprint for every custom dataset type. Before default routes added to this blueprint and it registered inside application this method is called. It can be used either for registration of the view function under new path or under existing path(like /new), in which case this new function will be used instead of default one.

Note, this blueprint has prefix /{package_type}/<id>/resource.

Return type:

flask.Blueprint

class ckan.plugins.interfaces.IValidators

Add extra validators to be returned by ckan.plugins.toolkit.get_validator().

get_validators() dict[str, Validator]

Return the validator functions provided by this plugin.

Return a dictionary mapping validator names (strings) to validator functions. For example:

{'valid_shoe_size': shoe_size_validator,
 'valid_hair_color': hair_color_validator}

These validator functions would then be available when a plugin calls ckan.plugins.toolkit.get_validator().

class ckan.plugins.interfaces.IResourceView

Add custom view renderings for different resource types.

info() dict[str, Any]

Returns a dictionary with configuration options for the view.

The available keys are:

Parameters:
  • name – name of the view type. This should match the name of the actual plugin (eg image_view or datatables_view).

  • title – title of the view type. Will be displayed on the frontend. This should be translatable (ie wrapped with toolkit._('Title')).

  • default_title – default title that will be used if the view is created automatically (optional, defaults to ‘View’).

  • default_description – default description that will be used if the view is created automatically (optional, defaults to ‘’).

  • icon – icon for the view type. Should be one of the Font Awesome types without the fa fa- prefix eg. compass (optional, defaults to ‘picture’).

  • always_available – the view type should be always available when creating new views regardless of the format of the resource (optional, defaults to False).

  • iframed – the view template should be iframed before rendering. You generally want this option to be True unless the view styles and JavaScript don’t clash with the main site theme (optional, defaults to True).

  • preview_enabled – the preview button should appear on the edit view form. Some view types have their previews integrated with the form (optional, defaults to False).

  • full_page_edit – the edit form should take the full page width of the page (optional, defaults to False).

  • schema

    schema to validate extra configuration fields for the view (optional). Schemas are defined as a dictionary, with the keys being the field name and the values a list of validator functions that will get applied to the field. For instance:

    {
        'offset': [ignore_empty, natural_number_validator],
        'limit': [ignore_empty, natural_number_validator],
    }
    

Example configuration object:

{'name': 'image_view',
 'title': toolkit._('Image'),
 'schema': {
    'image_url': [ignore_empty, unicode]
 },
 'icon': 'image',
 'always_available': True,
 'iframed': False,
 }
Returns:

a dictionary with the view type configuration

Return type:

dict

can_view(data_dict: dict[str, Any]) bool

Returns whether the plugin can render a particular resource.

The data_dict contains the following keys:

Parameters:
  • resource – dict of the resource fields

  • package – dict of the full parent dataset

Returns:

True if the plugin can render a particular resource, False otherwise

Return type:

bool

setup_template_variables(context: Context, data_dict: dict[str, Any]) dict[str, Any]

Adds variables to be passed to the template being rendered.

This should return a new dict instead of updating the input data_dict.

The data_dict contains the following keys:

Parameters:
  • resource_view – dict of the resource view being rendered

  • resource – dict of the parent resource fields

  • package – dict of the full parent dataset

Returns:

a dictionary with the extra variables to pass

Return type:

dict

view_template(context: Context, data_dict: dict[str, Any]) str

Returns a string representing the location of the template to be rendered when the view is displayed

The path will be relative to the template directory you registered using the add_template_directory() on the update_config method, for instance views/my_view.html.

Parameters:
  • resource_view – dict of the resource view being rendered

  • resource – dict of the parent resource fields

  • package – dict of the full parent dataset

Returns:

the location of the view template.

Return type:

string

form_template(context: Context, data_dict: dict[str, Any]) str

Returns a string representing the location of the template to be rendered when the edit view form is displayed

The path will be relative to the template directory you registered using the add_template_directory() on the update_config method, for instance views/my_view_form.html.

Parameters:
  • resource_view – dict of the resource view being rendered

  • resource – dict of the parent resource fields

  • package – dict of the full parent dataset

Returns:

the location of the edit view form template.

Return type:

string

class ckan.plugins.interfaces.IResourceController

Hook into the resource view.

before_resource_create(context: Context, resource: dict[str, Any]) None

Extensions will receive this before a resource is created.

Parameters:
  • context (dictionary) – The context object of the current request, this includes for example access to the model and the user.

  • resource (dictionary) – An object representing the resource to be added to the dataset (the one that is about to be created).

after_resource_create(context: Context, resource: dict[str, Any]) None

Extensions will receive this after a resource is created.

Parameters:
  • context (dictionary) – The context object of the current request, this includes for example access to the model and the user.

  • resource (dictionary) – An object representing the latest resource added to the dataset (the one that was just created). A key in the resource dictionary worth mentioning is url_type which is set to upload when the resource file is uploaded instead of linked.

before_resource_update(context: Context, current: dict[str, Any], resource: dict[str, Any]) None

Extensions will receive this before a resource is updated.

Parameters:
  • context (dictionary) – The context object of the current request, this includes for example access to the model and the user.

  • current (dictionary) – The current resource which is about to be updated

  • resource (dictionary) – An object representing the updated resource which will replace the current one.

after_resource_update(context: Context, resource: dict[str, Any]) None

Extensions will receive this after a resource is updated.

Parameters:
  • context (dictionary) – The context object of the current request, this includes for example access to the model and the user.

  • resource (dictionary) – An object representing the updated resource in the dataset (the one that was just updated). As with after_resource_create, a noteworthy key in the resource dictionary url_type which is set to upload when the resource file is uploaded instead of linked.

Note that the datastore will bypass this callback when updating the datastore_active flag on a resource that has been added to the datastore.

before_resource_delete(context: Context, resource: dict[str, Any], resources: list[dict[str, Any]]) None

Extensions will receive this before a resource is deleted.

Parameters:
  • context (dictionary) – The context object of the current request, this includes for example access to the model and the user.

  • resource (dictionary) – An object representing the resource that is about to be deleted. This is a dictionary with one key: id which holds the id string of the resource that should be deleted.

  • resources (list) – The list of resources from which the resource will be deleted (including the resource to be deleted if it existed in the dataset).

after_resource_delete(context: Context, resources: list[dict[str, Any]]) None

Extensions will receive this after a resource is deleted.

Parameters:
  • context (dictionary) – The context object of the current request, this includes for example access to the model and the user.

  • resources – A list of objects representing the remaining resources after a resource has been removed.

before_resource_show(resource_dict: dict[str, Any]) dict[str, Any]

Extensions will receive the validated data dict before the resource is ready for display.

Be aware that this method is not only called for UI display, but also in other methods, like when a resource is deleted, because package_show is used to get access to the resources in a dataset.

class ckan.plugins.interfaces.IGroupForm

Allows customisation of the group form and its underlying schema.

The behaviour of the plugin is determined by 5 method hooks:

  • group_form(self)

  • form_to_db_schema(self)

  • db_to_form_schema(self)

  • setup_template_variables(self, context, data_dict)

Furthermore, there can be many implementations of this plugin registered at once. With each instance associating itself with 0 or more group type strings. When a group form action is invoked, the group type determines which of the registered plugins to delegate to. Each implementation must implement these methods which are used to determine the group-type -> plugin mapping:

  • is_fallback(self)

  • group_types(self)

  • group_controller(self)

Implementations might want to consider mixing in ckan.lib.plugins.DefaultGroupForm which provides default behaviours for the 5 method hooks.

is_fallback() bool

Returns true if this provides the fallback behaviour, when no other plugin instance matches a group’s type.

There must be exactly one fallback view defined, any attempt to register more than one will throw an exception at startup. If there’s no fallback registered at startup the ckan.lib.plugins.DefaultGroupForm used as the fallback.

group_types() Iterable[str]

Returns an iterable of group type strings.

If a request involving a group of one of those types is made, then this plugin instance will be delegated to.

There must only be one plugin registered to each group type. Any attempts to register more than one plugin instance to a given group type will raise an exception at startup.

group_controller() str

Returns the name of the group view

The group view is the view, that is used to handle requests of the group type(s) of this plugin.

If this method is not provided, the default group view is used (group).

new_template(group_type: str) str

Returns a string representing the location of the template to be rendered for the ‘new’ page. Uses the default_group_type configuration option to determine which plugin to use the template from.

index_template(group_type: str) str

Returns a string representing the location of the template to be rendered for the index page. Uses the default_group_type configuration option to determine which plugin to use the template from.

read_template(group_type: str) str

Returns a string representing the location of the template to be rendered for the read page

history_template(group_type: str) str

Returns a string representing the location of the template to be rendered for the history page

edit_template(group_type: str) str

Returns a string representing the location of the template to be rendered for the edit page

group_form(group_type: str) str

Returns a string representing the location of the template to be rendered. e.g. group/new_group_form.html.

form_to_db_schema() Schema

Returns the schema for mapping group data from a form to a format suitable for the database.

db_to_form_schema() Schema

Returns the schema for mapping group data from the database into a format suitable for the form (optional)

setup_template_variables(context: Context, data_dict: dict[str, Any]) None

Add variables to c just prior to the template being rendered.

validate(context: Context, data_dict: DataDict, schema: Schema, action: str) tuple[dict[str, Any], dict[str, Any]] | None

Customize validation of groups.

When this method is implemented it is used to perform all validation for these groups. The default implementation calls and returns the result from ckan.plugins.toolkit.navl_validate.

This is an adavanced interface. Most changes to validation should be accomplished by customizing the schemas returned from form_to_db_schema() and db_to_form_schema() If you need to have a different schema depending on the user or value of any field stored in the group, or if you wish to use a different method for validation, then this method may be used.

Parameters:
  • context (dictionary) – extra information about the request

  • data_dict (dictionary) – the group to be validated

  • schema (dictionary) – a schema, typically from form_to_db_schema(), or db_to_form_schema()

  • action (string) – 'group_show', 'group_create', 'group_update', 'organization_show', 'organization_create' or 'organization_update'

Returns:

(data_dict, errors) where data_dict is the possibly-modified group and errors is a dictionary with keys matching data_dict and lists-of-string-error-messages as values

Return type:

(dictionary, dictionary)

prepare_group_blueprint(group_type: str, blueprint: Blueprint) Blueprint

Update or replace group blueprint for given group type.

Internally CKAN registers separate blueprint for every custom group type. Before default routes added to this blueprint and it registered inside application this method is called. It can be used either for registration of the view function under new path or under existing path(like /new), in which case this new function will be used instead of default one.

Note, this blueprint has prefix /{group_type}.

Return type:

flask.Blueprint

class ckan.plugins.interfaces.ITagController

Hook into the Tag view. These will usually be called just before committing or returning the respective object, i.e. when all validation, synchronization and authorization setup are complete.

before_view(tag_dict: dict[str, Any]) dict[str, Any]

Extensions will receive this before the tag gets displayed. The dictionary passed will be the one that gets sent to the template.

class ckan.plugins.interfaces.ITemplateHelpers

Add custom template helper functions.

By implementing this plugin interface plugins can provide their own template helper functions, which custom templates can then access via the h variable.

See ckanext/example_itemplatehelpers for an example plugin.

get_helpers() dict[str, Callable[[...], Any]]

Return a dict mapping names to helper functions.

The keys of the dict should be the names with which the helper functions will be made available to templates, and the values should be the functions themselves. For example, a dict like: {'example_helper': example_helper} allows templates to access the example_helper function via h.example_helper().

Function names should start with the name of the extension providing the function, to prevent name clashes between extensions.

By decorating a registered helper function with the ckan.plugins.toolkit.chained_helper decorator you can create a chain of helpers that are called in a sequence. This chain starts with the first chained helper to be registered and ends with the original helper (or a non-chained plugin override version). Chained helpers must accept an extra parameter, specifically the next helper in the chain, for example:

helper(next_helper, *args, **kwargs).

The chained helper function may call the next_helper function, optionally passing different values, handling exceptions, returning different values and/or raising different exceptions to the caller.

class ckan.plugins.interfaces.IFacets

Customize the search facets shown on search pages.

By implementing this interface plugins can customize the search facets that are displayed for filtering search results on the dataset search page, organization pages and group pages.

The facets_dict passed to each of the functions below is an OrderedDict in which the keys are CKAN’s internal names for the facets and the values are the titles that will be shown for the facets in the web interface. The order of the keys in the dict determine the order that facets appear in on the page. For example:

{'groups': _('Groups'),
 'tags': _('Tags'),
 'res_format': _('Formats'),
 'license': _('License')}

To preserve ordering, make sure to add new facets to the existing dict rather than updating it, ie do this:

facets_dict['groups'] = p.toolkit._('Publisher')
facets_dict['secondary_publisher'] = p.toolkit._('Secondary Publisher')

rather than this:

facets_dict.update({
   'groups': p.toolkit._('Publisher'),
   'secondary_publisher': p.toolkit._('Secondary Publisher'),
})

Dataset searches can be faceted on any field in the dataset schema that it makes sense to facet on. This means any dataset field that is in CKAN’s Solr search index, basically any field that you see returned by package_show().

If there are multiple IFacets plugins active at once, each plugin will be called (in the order that they’re listed in the CKAN config file) and they will each be able to modify the facets dict in turn.

dataset_facets(facets_dict: OrderedDict[str, Any], package_type: str) OrderedDict[str, Any]

Modify and return the facets_dict for the dataset search page.

The package_type is the type of dataset that these facets apply to. Plugins can provide different search facets for different types of dataset. See IDatasetForm.

Parameters:
  • facets_dict (OrderedDict) – the search facets as currently specified

  • package_type (string) – the dataset type that these facets apply to

Returns:

the updated facets_dict

Return type:

OrderedDict

group_facets(facets_dict: OrderedDict[str, Any], group_type: str, package_type: str | None) OrderedDict[str, Any]

Modify and return the facets_dict for a group’s page.

The package_type is the type of dataset that these facets apply to. Plugins can provide different search facets for different types of dataset. See IDatasetForm.

The group_type is the type of group that these facets apply to. Plugins can provide different search facets for different types of group. See IGroupForm.

Parameters:
  • facets_dict (OrderedDict) – the search facets as currently specified

  • group_type (string) – the group type that these facets apply to

  • package_type (string) – the dataset type that these facets apply to

Returns:

the updated facets_dict

Return type:

OrderedDict

organization_facets(facets_dict: OrderedDict[str, Any], organization_type: str, package_type: str | None) OrderedDict[str, Any]

Modify and return the facets_dict for an organization’s page.

The package_type is the type of dataset that these facets apply to. Plugins can provide different search facets for different types of dataset. See IDatasetForm.

The organization_type is the type of organization that these facets apply to. Plugins can provide different search facets for different types of organization. See IGroupForm.

Parameters:
  • facets_dict (OrderedDict) – the search facets as currently specified

  • organization_type (string) – the organization type that these facets apply to

  • package_type (string) – the dataset type that these facets apply to

Returns:

the updated facets_dict

Return type:

OrderedDict

class ckan.plugins.interfaces.IAuthenticator

Allows custom authentication methods to be integrated into CKAN.

All interface methods except for the abort() one support returning a Flask response object. This can be used for instance to issue redirects or set cookies in the response. If a response object is returned there will be no further processing of the current request and that response will be returned. This can be used by plugins to:

  • Issue a redirect:

    def identify(self):
    
        return toolkit.redirect_to('myplugin.custom_endpoint')
    
  • Set or clear cookies (or headers):

    from Flask import make_response
    
    def identify(self)::
    
        response = make_response(toolkit.render('my_page.html'))
        response.set_cookie(cookie_name, expires=0)
    
        return response
    
identify() Response | None

Called to identify the user.

If the user is identified then it should set:

  • g.user: The name of the user

  • g.userobj: The actual user object

Alternatively, plugins can return a response object in order to prevent the default CKAN authorization flow. See the IAuthenticator documentation for more details.

login() Response | None

Called before the login starts (that is before asking the user for user name and a password in the default authentication).

Plugins can return a response object to prevent the default CKAN authorization flow. See the IAuthenticator documentation for more details.

logout() Response | None

Called before the logout starts (that is before clicking the logout button in the default authentication).

Plugins can return a response object to prevent the default CKAN authorization flow. See the IAuthenticator documentation for more details.

abort(status_code: int, detail: str, headers: dict[str, Any] | None, comment: str | None) tuple[int, str, dict[str, Any] | None, str | None]

Called on abort. This allows aborts due to authorization issues to be overridden

authenticate(identity: Mapping[str, Any]) User | None

Called before the authentication starts (that is after clicking the login button)

Plugins should return a user object if the authentication was successful, or None` otherwise.

class ckan.plugins.interfaces.ITranslation

Allows extensions to provide their own translation strings.

i18n_directory() str

Change the directory of the .mo translation files

i18n_locales() list[str]

Change the list of locales that this plugin handles

i18n_domain() str

Change the gettext domain handled by this plugin

class ckan.plugins.interfaces.IUploader

Extensions implementing this interface can provide custom uploaders to upload resources and group images.

get_uploader(upload_to: str, old_filename: str | None) PUploader | None

Return an uploader object to upload general files that must implement the following methods:

__init__(upload_to, old_filename=None)

Set up the uploader.

Parameters:
  • upload_to (string) – name of the subdirectory within the storage directory to upload the file

  • old_filename (string) – name of an existing image asset, so the extension can replace it if necessary

update_data_dict(data_dict, url_field, file_field, clear_field)

Allow the data_dict to be manipulated before it reaches any validators.

Parameters:
  • data_dict (dictionary) – data_dict to be updated

  • url_field (string) – name of the field where the upload is going to be

  • file_field (string) – name of the key where the FieldStorage is kept (i.e the field where the file data actually is).

  • clear_field (string) – name of a boolean field which requests the upload to be deleted.

upload(max_size)

Perform the actual upload.

Parameters:

max_size (int) – upload size can be limited by this value in MBs.

get_resource_uploader(resource: dict[str, Any]) PResourceUploader | None

Return an uploader object used to upload resource files that must implement the following methods:

__init__(resource)

Set up the resource uploader.

Parameters:

resource (dictionary) – resource dict

Optionally, this method can set the following two attributes on the class instance so they are set in the resource object:

  • filesize (int): Uploaded file filesize.

  • mimetype (str): Uploaded file mimetype.

upload(id, max_size)

Perform the actual upload.

Parameters:
  • id (string) – resource id, can be used to create filepath

  • max_size (int) – upload size can be limited by this value in MBs.

get_path(id)

Required by the resource_download action to determine the path to the file.

Parameters:

id (string) – resource id

class ckan.plugins.interfaces.IBlueprint

Register an extension as a Flask Blueprint.

get_blueprint() list[Blueprint] | Blueprint

Return either a single Flask Blueprint object or a list of Flask Blueprint objects to be registered by the app.

class ckan.plugins.interfaces.IPermissionLabels

Extensions implementing this interface can override the permission labels applied to datasets to precisely control which datasets are visible to each user.

Implementations might want to consider mixing in ckan.lib.plugins.DefaultPermissionLabels which provides default behaviours for these methods.

See ckanext/example_ipermissionlabels for an example plugin.

get_dataset_labels(dataset_obj: model.Package) list[str]

Return a list of unicode strings to be stored in the search index as the permission lables for a dataset dict.

Parameters:

dataset_obj (Package model object) – dataset details

Returns:

permission labels

Return type:

list of unicode strings

get_user_dataset_labels(user_obj: 'model.User' | None) list[str]

Return the permission labels that give a user permission to view a dataset. If any of the labels returned from this method match any of the labels returned from get_dataset_labels() then this user is permitted to view that dataset.

Parameters:

user_obj (User model object or None) – user details

Returns:

permission labels

Return type:

list of unicode strings

class ckan.plugins.interfaces.IForkObserver

Observe forks of the CKAN process.

before_fork() None

Called shortly before the CKAN process is forked.

class ckan.plugins.interfaces.IApiToken

Extend functionality of API Tokens.

This interface is unstable and new methods may be introduced in future. Always use inherit=True when implementing it.

Example:

p.implements(p.IApiToken, inherit=True)
create_api_token_schema(schema: Schema) Schema

Return the schema for validating new API tokens.

Parameters:

schema (dict) – a dictionary mapping api_token dict keys to lists of validator and converter functions to be applied to those keys

Returns:

a dictionary mapping api_token dict keys to lists of validator and converter functions to be applied to those keys

Return type:

dict

decode_api_token(encoded: str, **kwargs: Any) dict[str, Any] | None

Make an attempt to decode API Token provided in request.

Decode token if it possible and return dictionary with mandatory jti key(token id for DB lookup) and optional additional items, which will be used further in preprocess_api_token.

Parameters:
  • encoded (str) – API Token provided in request

  • kwargs (dict) – any additional parameters that can be added in future or by plugins. Current implementation won’t pass any additional fields, but plugins may use this feature, passing JWT aud or iss claims, for example

Returns:

dictionary with all the decoded fields or None

Return type:

dict | None

encode_api_token(data: dict[str, Any], **kwargs: Any) str | None

Make an attempt to encode API Token.

Encode token if it possible and return string, that will be shown to user.

Parameters:
  • data (dict) – dictionary, containing all postprocessed data

  • kwargs (dict) – any additional parameters that can be added in future or by plugins. Current implementation won’t pass any additional fields, but plugins may use this feature, passing JWT aud or iss claims, for example

Returns:

token as encodes string or None

Return type:

str | None

preprocess_api_token(data: Mapping[str, Any]) Mapping[str, Any]

Handle additional info from API Token.

Allows decoding or extracting any kind of additional information from API Token, before it used for fetching current user from database.

Parameters:

data (dict) – dictionary with all fields that were previously created in postprocess_api_token (potentially modified by some other plugin already.)

Returns:

dictionary that will be passed into other plugins and, finally, used for fetching User instance

Return type:

dict

postprocess_api_token(data: dict[str, Any], jti: str, data_dict: dict[str, Any]) dict[str, Any]

Encode additional information into API Token.

Allows passing any kind of additional information into API Token or performing side effects, before it shown to user.

Parameters:
  • data (dict) – dictionary representing newly generated API Token. May be already modified by some plugin.

  • jti (str) – Id of the token

  • data_dict (dict) – data used for token creation.

Returns:

dictionary with fields that will be encoded into final API Token

Return type:

dict

add_extra_fields(data_dict: dict[str, Any]) dict[str, Any]

Provide additional information alongside with API Token.

Any extra information that is not itself a part of a token, but can extend its functionality(for example, refresh token) is registered here.

Parameters:

data_dict (dict) – dictionary that will bre returned from api_token_create API call.

Returns:

dictionary with token and optional set of extra fields.

Return type:

dict

class ckan.plugins.interfaces.IClick

Allow extensions to define click commands.

get_commands() list[click.Command]

Return a list of command functions objects to be registered by the click.add_command.

Example:

p.implements(p.IClick)
# IClick
def get_commands(self):
    """Call me via: `ckan hello`"""
    import click
    @click.command()
    def hello():
        click.echo('Hello, World!')
    return [hello]
Returns:

command functions objects

Return type:

list of function objects

class ckan.plugins.interfaces.ISignal

Subscribe to CKAN signals.

get_signal_subscriptions() Dict[Signal, Iterable[Any | Dict[str, Any]]]

Return a mapping of signals to their listeners.

Note that keys are not strings, they are instances of blinker.Signal. When using signals provided by CKAN core, it is better to use the references from the plugins toolkit for better future compatibility. Values should be a list of listener functions:

def get_signal_subscriptions(self):
    import ckan.plugins.toolkit as tk

    # or, even better, but requires additional dependency:
    # pip install ckantoolkit
    import ckantoolkit as tk

    return {
        tk.signals.request_started: [request_listener],
        tk.signals.register_blueprint: [
            first_blueprint_listener,
            second_blueprint_listener
        ]
    }

Listeners are callables that accept one mandatory argument (sender) and an arbitrary number of named arguments (text). The best signature for a listener is def(sender, **kwargs).

The sender argument will be different depending on the signal and will be generally used to conditionally executing code on the listener. For example, the register_blueprint signal is sent every time a custom dataset/group/organization blueprint is registered (using ckan.plugins.interfaces.IDatasetForm or ckan.plugins.interfaces.IGroupForm). Depending on the kind of blueprint, sender may be ‘dataset’, ‘group’, ‘organization’ or ‘resource’. If you want to do some work only for ‘dataset’ blueprints, you may end up with something similar to:

import ckan.plugins.toolkit as tk

def dataset_blueprint_listener(sender, **kwargs):
    if sender != 'dataset':
        return
    # Otherwise, do something..

class ExamplePlugin(plugins.SingletonPlugin)
    plugins.implements(plugins.ISignal)

    def get_signal_subscriptions(self):

        return {
            tk.signals.register_blueprint: [
                dataset_blueprint_listener,
            ]
        }

Because this is a really common use case, there is additional form of listener registration supported. Instead of just callables, one can use dictionaries of form {'receiver': CALLABLE, 'sender': DESIRED_SENDER}. The following code snippet has the same effect than the previous one:

import ckan.plugins.toolkit as tk

def dataset_blueprint_listener(sender, **kwargs):
    # do something..

class ExamplePlugin(plugins.SingletonPlugin)
    plugins.implements(plugins.ISignal)

    def get_signal_subscriptions(self):

        return {
            tk.signals.register_blueprint: [{
                'receiver': dataset_blueprint_listener,
                'sender': 'dataset'
            }]
        }

The two forms of registration can be mixed when multiple listeners are registered, callables and dictionaries with receiver/sender keys:

import ckan.plugins.toolkit as tk

def log_registration(sender, **kwargs):
    log.info("Log something")

class ExamplePlugin(plugins.SingletonPlugin)
    plugins.implements(plugins.ISignal)

    def get_signal_subscriptions(self):
        return {
            tk.signals.request_started: [
                log_registration,
                {'receiver': log_registration, 'sender': 'dataset'}
            ]
        }

Even though it is possible to change mutable arguments inside the listener, or return something from it, the main purpose of signals is the triggering of side effects, like logging, starting background jobs, calls to external services, etc.

Any mutation or attempt to change CKAN behavior through signals should be considered unsafe and may lead to hard to track bugs in the future. So never modify the arguments of signal listener and treat them as constants.

Always check for the presence of the desired value inside the received context (named arguments). Arguments passed to signals may change over time, and some arguments may disappear.

Returns:

mapping of subscriptions to signals

Return type:

dict