How to make configuration objects exportable with CTools
Drupal 7 will no longer be supported after January 5, 2025. Learn more and find resources for Drupal 7 sites
Many modules have a concept of configuration presets – collections of configuration or other types of objects that administrators set up that can then be used in different contexts on a website. A well-known example is ImageCache which has its image manipulation presets that allow you to set up groups of image operations that can be applied to images on different parts of the site. In the Views module, administrators define views – objects that are containers for some very complex configuration. These configuration objects are all stored in the database as they are created, but they can also be exported into the code, and this is the foundation of the concept of exportables.
With exportables, the exported code of a configuration object can be used in different ways, where the simplest option usually is to paste it into the module's import page to restore a previously exported object back into the database. However, a more powerful way to use exportables is to use the exported code as the actual storage for the configuration, rather than just a source for importing it into the database. This is done by creating a custom, usually site-specific module, which implements certain hooks that expose the exported configuration objects to the running Drupal site.
Benefits of using exportables
The advantage of not having the configuration defined in the database is that it's easier to migrate a piece of configuration between, say, a staging and a production site. The configuration can also be kept in version control which makes development and deployment a lot more manageable.
Once an exported configuration object is defined in code, administrators can still make changes to it, which makes them become overridden. This means there is a newer version of the configuration in the database which will take precedence over the one stored in code. There are usually options to revert the configuration to the exported version, or to re-export the overridden version. The Features module is a very convenient tool for managing this process.
Implementing exportable configuration
These are the basics of exportables. Now, let's take a look at how a module can make its configuration presets exportable with minimal effort using CTools' Export and Export-UI APIs. This page is largely based on this blog post by zoo33 which contains a little more detail. The main references for this subject are:
- The export.html and export-ui.html help files in your CTools module directory.
- api.drupal.org and/or drupalcontrib.org for learning about all available API functions.
Declaring the CTools dependency
Declare that your module depends on CTools by adding the following line you the .info
file:
dependencies[] = ctools
Defining the configuration data
Define the table structure for the configuration objects using hook_schema()
. CTools requires that the exportables live in their own database table and have at least a machine-readable name field. In the example code, we'll be using the module name "Mymodule" which will obviously have to be replaced with the name of your module. Here is the example mymodule.install
file:
<?php
/**
* Implementation of hook_schema().
*/
function mymodule_schema() {
$schema['mymodule_preset'] = array(
'description' => t('Table storing preset definitions.'),
'export' => array(
'key' => 'name',
'key name' => 'Name',
'primary key' => 'pid',
'identifier' => 'preset', // Exports will be defined as $preset
'default hook' => 'default_mymodule_preset', // Function hook name.
'api' => array(
'owner' => 'mymodule',
'api' => 'default_mymodule_presets', // Base name for api include files.
'minimum_version' => 1,
'current_version' => 1,
),
),
'fields' => array(
'name' => array(
'type' => 'varchar',
'length' => '255',
'description' => 'Unique ID for presets. Used to identify them programmatically.',
),
'pid' => array(
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
'description' => 'Primary ID field for the table. Not used for anything except internal lookups.',
'no export' => TRUE, // Do not export database-only keys.
),
'admin_title' => array(
'type' => 'varchar',
'length' => '255',
'description' => 'A human readable name of a preset.',
),
'mydata' => array(
'type' => 'text',
'size' => 'big',
'description' => 'My exportable configuration data.',
),
),
'primary key' => array('pid'),
'unique keys' => array(
'name' => array('name'),
),
);
return $schema;
}
?>
In Drupal 6.x, drupal_install_schema()
and drupal_uninstall_schema()
must also be called explicitly:
<?php
/**
* Implementation of hook_install().
*/
function mymodule_install() {
drupal_install_schema('mymodule');
}
/**
* Implementation of hook_uninstall().
*/
function mymodule_uninstall() {
drupal_uninstall_schema('mymodule');
}
?>
(In Drupal 7.x, these functions are automatically called implicitly.)
This is mostly a standard .install
file, except for the export
part of the schema
array. This is specific to ctool's Export API, and it defines how this table's content will be imported and exported. mydata
is the table field where the actual configuration data will be saved. Of course, modules can define as many fields as they want for this purpose.
Defining default presets
In order to provide some default configuration presets for the module, you can implement a couple of hooks that CTools provides. These presets will show up in the admin interface without the user having to do anything other than activating the module. This is also how third-party modules (and features) would define their own presets for Mymodule.
<?php
/**
* Implementation of hook_ctools_plugin_api().
*
* Tell CTools that we support the default_mymodule_presets API.
*/
function mymodule_ctools_plugin_api($owner, $api) {
if ($owner == 'mymodule' && $api == 'default_mymodule_presets') {
return array('version' => 1);
}
}
/**
* Implementation of hook_default_mymodule_preset().
*
* Provide a couple of default presets.
*/
function mymodule_default_mymodule_preset() {
$export = array();
$preset = new stdClass;
$preset->api_version = 1;
$preset->name = 'my_default_preset';
$preset->admin_title = 'Default preset';
$preset->mydata = 'x';
$export['my_default_preset'] = $preset;
return $export;
}
?>
The administration interface
In order for CTools to be able to provide an interface for administering the configuration presets, we have to expose the preset schema to the Export UI. This is done by telling CTools that we want to define an 'export_ui' plugin (in the mymodule.module file):
<?php
/**
* Implementation of hook_ctools_plugin_directory().
*/
function mymodule_ctools_plugin_directory($module, $plugin_type) {
// Load the export_ui plugin.
if ($plugin_type == 'export_ui') {
return 'plugins/export_ui';
}
}
?>
The return value tells CTools to look for the plugin in the directory plugins/export_ui within the module directory, so this directory has to be created. Within this directory, create a file called mymodule_ctools_export_ui.inc. The directory structure should then look like this:
mymodule.install
mymodule.module
plugins/
- export_ui/
- - mymodule_ctools_export_ui.inc
In the new file, add the plugin definition:
<?php
/**
* Define this Export UI plugin.
*/
$plugin = array(
'schema' => 'mymodule_preset', // As defined in hook_schema().
'access' => 'administer mymodule', // Define a permission users must have to access these pages.
// Define the menu item.
'menu' => array(
'menu item' => 'mymodule',
'menu title' => 'Mymodule',
'menu description' => 'Administer Mymodule presets.',
),
// Define user interface texts.
'title singular' => t('preset'),
'title plural' => t('presets'),
'title singular proper' => t('Mymodule preset'),
'title plural proper' => t('Mymodule presets'),
// Define the names of the functions that provide the add/edit forms.
'form' => array(
'settings' => 'mymodule_ctools_export_ui_form',
// 'submit' and 'validate' are also valid callbacks.
),
);
?>
The code comments should give an idea of what is going on. Finally, in the same plugin file, add the form function for adding and editing the configuration objects, as well as any submit/validate functions that you've specified in the plugin definition. The form can be as complicated as needed, depending on the complexity of the configuration.
<?php
/**
* Define the preset add/edit form.
*/
function mymodule_ctools_export_ui_form(&$form, &$form_state) {
$preset = $form_state['item'];
// Add Mymodule's configuration interface.
$form['mydata'] = array(
'#type' => 'textfield',
'#title' => t('Mymodule configuration'),
'#description' => t('This is just the simplest possible example of a configuration interface.'),
'#default_value' => $preset->mydata,
'#required' => true,
);
}
?>
This is all that is needed to provide a fully functional administration interface and exportability for your configuration presets. There is a lot more you can do to customize how the administration pages work. See the export-ui.html file for more information!
Help improve this page
You can:
- Log in, click Edit, and edit this page
- Log in, click Discuss, update the Page status value, and suggest an improvement
- Log in and create a Documentation issue with your suggestion