In this blog post I will try provide an example on how you could make your modules extendable within Drupal 7.
This is a very simple module that displays buttons with labels from your custom plugins on http://localhost/my-module/plugin page and with the configuration form here: http://localhost/admin/config/development/my-module.
Hopefully this is going to be a very useful tutorial for you and help you to make your modules extendable.
Tutorial
First lets create a custom module with the following structure:
my_module
my_module/my_module.info
my_module/my_module.module
my_module/plugins/plugin.php
my_module/plugins/example.php
In the my_module.info file implement your custom hook.
IMPORTANT: make sure you clear Drupal cache after you add new plugin to .info files[].
name = My Module
description = Description of the module
core = 7.x
configure = admin/config/development/my-module
; This is required. You have to include all plugin files.
files[] = plugins/plugin.php
files[] = plugins/example.php
In the my_module.module file implement your custom hook.
/**
* Implements hook_menu().
*/
function my_module_menu() {
// My module configuration page.
$items['admin/config/development/my-module'] = array(
'title' => 'My Module',
'description' => 'Description of the module',
'page callback' => 'drupal_get_form',
'page arguments' => array('my_module_configuration_form'),
'access arguments' => array('administer site configuration'),
'type' => MENU_NORMAL_ITEM,
);
// Path for the plugin integration page.
// This page will display buttons from
// all enabled plugins.
$items['my-module/plugin'] = array(
'title' => 'My Module Elements',
'description' => 'Description of the module',
'page callback' => 'drupal_get_form',
'page arguments' => array('my_module_view_form'),
'access callback' => TRUE,
'type' => MENU_NORMAL_ITEM,
);
return $items;
}
/**
* Implements hook_my_own_hook().
*/
function my_module_my_own_hook() {
return array(
// Machine name of the plugin.
'example' => array(
// Human readable string visible on the config page.
'name' => t('Example plugin'),
// PHP Class name that extends MyPluginBase class.
'phpClassName' => 'Example',
),
);
}
/**
* View form.
* Simple callback that implements buttons with labels
* pulled from plugin `label()` methods.
*/
function my_module_view_form($form, &$form_state) {
// Load all plugins.
$plugins = my_module_load_plugins();
foreach ($plugins as $name => $plugin) {
// Display only enabled plugins.
$enabled = variable_get(
'my_module_' . $name . '_enabled',
FALSE
);
if ($enabled) {
// Get button label.
$label = my_module_plugin_method(
$plugin['phpClassName'],
'label'
);
$form[$name] = array(
'#type' => 'submit',
'#value' => $label,
);
}
}
return $form;
}
/**
* Configuration form.
*/
function my_module_configuration_form($form, &$form_state) {
$form['settings'] = array(
'#type' => 'vertical_tabs',
);
// Load all plugins.
$plugins = my_module_load_plugins();
foreach ($plugins as $name => $plugin) {
$form['settings'][$name] = array(
'#type' => 'fieldset',
'#title' => $plugin['name'],
);
// Plugin status.
$form['settings'][$name][$name . '_enabled'] = array(
'#type' => 'checkbox',
'#title' => t('Enable plugin'),
'#default_value' => variable_get(
'my_module_' . $name . '_enabled',
FALSE
),
'#description' => t("Description for the checkbox."),
);
// Check if plugin has configuraiton form.
$configForm = my_module_plugin_method(
$plugin['phpClassName'],
'configForm',
array($form_state)
);
if (is_array($configForm)) {
$form['settings'][$name] += $configForm;
}
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save settings'),
);
return $form;
}
/**
* Form validation handler.
*/
function my_module_configuration_form_validate($form, &$form_state) {
$plugins = my_module_load_plugins();
foreach ($plugins as $name => $plugin) {
// Trigger plugin validation handler method.
my_module_plugin_method(
$plugin['phpClassName'],
'validationHandler',
array($form, $form_state)
);
}
}
/**
* Form submit handler.
*/
function my_module_configuration_form_submit($form, &$form_state) {
$plugins = my_module_load_plugins();
foreach ($plugins as $name => $plugin) {
// Save Enable options.
variable_get(
'my_module_' . $name . '_enabled',
$form_state['values'][$name . '_enabled']
);
// Trigger plugin submit handler method.
my_module_plugin_method(
$plugin['phpClassName'],
'submitHandler',
array($form, $form_state)
);
}
drupal_set_message(t('The configuration options have been saved.'));
}
/**
* Helper function to invoke your custom hook.
*
* @return array List of available plugins.
*/
function my_module_load_plugins() {
$plugins = array();
// Invoke `my_own_hook` implemented in all modules.
$plugin_hooks = module_invoke_all('my_own_hook');
foreach ($plugin_hooks as $name => $plugin) {
// @see http://php.net/manual/en/class.reflectionclass.php
$reflection = new ReflectionClass($plugin['phpClassName']);
// Now make sure the plugin class is extended from
// your abstract `MyPluginBase` class.
if ($reflection->isSubclassOf('MyPluginBase')) {
$plugins[$name] = $plugin;
}
}
return $plugins;
}
/**
* Helper function to call phpClass method.
*
* @return mixed Result of the class method.
*/
function my_module_plugin_method($class, $method, $args = array()) {
$reflection = new ReflectionClass($class);
$method = $reflection->getMethod($method);
$pluginClass = new $class();
return $method->invokeArgs($pluginClass, $args);
}
The plugin.php is going to be an abstract class for the plugin.
abstract class MyPluginBase {
/**
* Button label.
*/
abstract public function label();
/**
* Configuration form.
* Drupal form API elements.
*
* @return array.
*/
abstract public function configForm(&$form_state);
/**
* Drupal form validation handler.
*/
abstract public function validationHandler($form, &$form_state);
/**
* Drupal form submit handler.
*/
abstract public function submitHandler($form, &$form_state);
}
The example.php is your first example plugin that uses MyPluginBase class.
/**
* Example plugin.
*/
class Example extends MyPluginBase {
/**
* {@inheritdoc}
*/
public function label() {
return t('Example');
}
/**
* {@inheritdoc}
*/
public function configForm(&$form_state) {
$form['example_setting'] = array(
'#title' => t('Example variable'),
'#type' => 'textfield',
'#default_value' => variable_get('my_module_example_setting', ''),
'#description' => t('Description of the configraiont option.'),
);
return $form;
}
/**
* {@inheritdoc}
*/
public function validationHandler($form, &$form_state) {
if (empty($form_state['values']['example_setting'])) {
form_set_error('example_setting', t('Validation error'));
}
}
/**
* {@inheritdoc}
*/
public function submitHandler($form, &$form_state) {
variable_set(
'my_module_example_setting',
$form_state['values']['example_setting']
);
}
}