Understanding the development of Drupal's module is also equals to understand a concept of node (content type), a hook system (callback function named 'hook'), and a form ($form).
I have already explained Drupal's hook system in "Drupal: a Minimum Set of Hooks to Develop a Module". So let me try to implement a module concretely here. The module name is 'hook'.
It is necessary to implement a module with four files such as yourmodule.info, yourmodule.install, yourmodule.module, yourmodule.css since Drupal 5. Therefore, in case of a developement of a module called 'hook', those four files must be created. This hook module includes a set of fields such as a title, a body and a unique value hid (= hook ID) with one of letters from 'A' to 'E' in this 'hook' module. A letter 'A' to 'E' to be displayed will be stored the corresponding a value from one to five in a database. The default value of hid can be set in https://yourdomain.com/admin/settings/hook. The following is an example of the 'hook' module.
hook.info
; Semi-colon is a comment.
name = Hook
description = "A module template."
package =
version = VERSION
;
version = "5.x-1.x-dev"
project = "Example"
hook.install
<?php
/**
* Implementation of hook_install()
* Bind into Drupal's module system admin/build/modules.
* Basically make a database table which stores data handled by this module.
*/
function hook_install() {
switch ($GLOBALS['db_type']) {
case 'mysql':
case 'mysqli':
db_query("CREATE TABLE `hook`
(
`nid` INT( 10 ) UNSIGNED NOT NULL ,
`hid` INT( 3 ) UNSIGNED NOT NULL DEFAULT '1',
PRIMARY KEY ( `nid` )
) ENGINE = MYISAM"
);
break;
case 'pgsql':
break;
}
}
/**
* Implementation of hook_uninstall()
* Drop a table that this module was using.
* Also delete a set of values in this module by variable_del
*/
function hook_uninstall() {
db_query('DROP TABLE {hook}');
variable_del('hook_option'); // Doi NOT Forget!
}
?>
hook.module
<?php
// Define constant strings
define('MODULE_NAME', 'Hook');
define('PRIVILEGE_ADMIN' , 'administer' . MODULE_NAME . ' server settings');
define('PRIVILEGE_CREATE' , 'create' . MODULE_NAME . ' forms and docs');
define('PRIVILEGE_VIEW' , 'view' . MODULE_NAME . ' forms and docs');
define('PRIVILEGE_EDIT' , 'edit own' . MODULE_NAME . ' forms and docs');
/**
* The implementation of hook_help()
* Explain the URL in every single case statement
*/
function hook_help($section) {
switch ($section) {
case 'admin/help#hook':
$o .= '<p>' . t('Submit, view and manage hook lists.') . '</p>';
return $o;
case 'admin/modules#description':
return t('Allows users to submit, view and manage job records');
case 'node/add#hook':
return t('Add a ' . MODULE_NAME . ' record.');
}
}
/**
* The implementation of hook_perm()
* The definitions here may use access controls in admin/user/access
*/
function hook_perm() {
return array(
PRIVILEGE_ADMIN,
PRIVILEGE_CREATE,
PRIVILEGE_VIEW,
PRIVILEGE_EDIT
);
}
/**
* The implementation of hook_access()
* Do access control of create, update, delete, view.
* @param op four values such as create, view, update, delete
*/
function hook_access($op, $node) {
global $user;
if ($op == 'create') {
return user_access(PRIVILEGE_CREATE);
}
if ($op == 'view') {
return user_access(PRIVILEGE_VIEW);
}
if ($op == 'update' || $op == 'delete') {
if (user_access(PRIVILEGE_EDIT)
&& ($user->uid == $node->uid)) {
return TRUE;
}
}
}
/**
* The implementaiton of hook_menu()
* Describe Drupal's menus (paths), corresponding functions called by clicking it,
* and the combinations of access control.
*/
function hook_menu($may_cache) {
$items = array();
if ($may_cache) {
$items[] = array(
'path' => 'node/add/hook',
'title' => t('Hook'),
'access' => user_access(PRIVILEGE_CREATE)
);
$items[] = array(
'type' => MENU_NORMAL_ITEM,
'path' => 'admin/settings/hook',
'title' => t(MODULE_NAME . ' default settings'),
'access' => user_access(PRIVILEGE_ADMIN),
'callback' => 'drupal_get_form',
'callback arguments' => 'hook_admin',
'description' => 'Configure Hook default settings'
);
}
return $items;
}
/**
* The implementation of hook_node_info()
* Define content types. Put a module name into 'hook'.
* 'hook' may not be only this module name but also different one.
* Allow to realize several forms in this one module by adding a set of
* hook, name, module, description into array
* Must keep a set of hook, name, module, description.
*/
function hook_node_info() {
return array(
'hook' => array(
'name' => t('Hook'),
'module' => 'hook',
'description' => t('You can make a hook record and view them.')
)
);
}
/**
* The implementaiton of hook_admin()
* Allow to define multiple unique default values in admin/settings/hook
* by implementing this hook.
*/
function hook_admin() {
// hostname
$form['hook_option'] = array(
'#type' => 'select',
'#title' => t('hid default value'),
'#default_value' => variable_get('hook_option', 1),
'#options' => array('1'=>'A', '2'=>'B', '3'=>'C', '4'=>'D', '5'=>'E'),
'#size' => 1,
'#weight' => -10,
'#required' => TRUE
);
return system_settings_form($form);
}
/**
* The implementation of hook_form()
* Define a form.
* @param node An instance of stored node when editting
* @return $form Drupal will make a form automatically when a form object is returned.
*/
function hook_form(&$node) {
// $form['hid']
$hid = $node->hid ? $node->hid : variable_get('hook_option', 1);
$form['title'] = array(
'#type' => 'textfield',
'#title' => t('Title'),
'#default_value' => $node->title,
'#weight' => -8,
'#required' => TRUE,
);
$form['hid'] = array(
'#type' => 'select',
'#title' => t('Hook hid value'),
'#options' => array('1'=>'A', '2'=>'B', '3'=>'C', '4'=>'D', '5'=>'E'),
'#default_value' => $hid,
'#weight' => -6,
'#required' => TRUE
);
// full description / body
$form['body_filter']['body'] = array(
'#type' => 'textarea',
'#title' => t('Body'),
'#rows' => 4,
'#default_value' => $node->body,
'#weight' => -5,
'#required' => TRUE
);
$form['body_filter']['format'] = filter_form($node->format);
return $form;
}
/**
* The implementation of hook_view()
* Display values stored in a form.
* @param &$node An object to be displayed
* @param $teaser boolean: Is a teaser or a full node?
* @param $page boolean: Is the own page displayed?
*/
function hook_view(&$node, $teaser = FALSE, $page = FALSE) {
$node = node_prepare($node, $teaser);
$node->content['title'] = array(
'#value' => theme('hook_title', $node->title),
'#weight' => -8
);
$node->content['hid'] = array(
'#value' => theme('hook_hid', $node->hid),
'#weight' => -6
);
$node->content['body'] = array(
'#value' => theme('hook_body', $node->body),
'#weight' => -4
);
return $node;
}
/**
* The implementation of hook_insert()
* Called when a submit button is pressed in a form of creating a new content.
* Store values to your own prepared fields. basic node information will be
* stored by Drupal.
* nid is also necessary as a primary key in your prepared table because nid which
* stands for 'node ID' is making a relation in between Drupal's node and your own data.
*/
function hook_insert($node) {
db_query(
"INSERT INTO {hook} (`nid`, `hid`) VALUES ( %d, %d )",
$node->nid, $node->hid
);
}
/**
* The implementation of hook_update()
* Called when a submit button is pressed in a form with editting mode.
*/
function hook_update($node) {
db_query("UPDATE {hook} set `hid`=%d WHERE nid=%d", $node->hid, $node->nid);
}
/**
* The implementation of hook_delete()
* Called when a delete button is pressed in a form.
*/
function hook_delete(&$node) {
db_query('DELETE FROM {hook} WHERE `nid`=%d', $node->nid);
}
/**
* The implementation of hook_load()
* Store data that you want to process in your module into your prepared table
* by yourself (hook_insert, hook_update).
* Drupal will automatically process node information which is a minimum set of content,
* however, it is necessary to tell Drupal the differentical information in between
* a Drupal's node and your module's processing data in a field in your table
* when you want to display your data in your module. that your module processes.
* This is the implementation of the function.
*/
function hook_load($node) {
$r = db_fetch_object(
db_query('SELECT `hid` FROM {hook} WHERE nid=%d', $node->nid)
);
return $r;
}
/**
* The implementation of hook_validate()
* Called before hook_insert or hook_update are executed.
* Validate the inputted vlaues sent from a form to this module
* by implement this hook function.
*/
function hook_validate($form_id, $form_values) {
// dump the form validate variables
//print '<code>' . print_r($form_values, true); exit();
}
/**
* The implementation of theme_hook_title()
* Link to the style sheet when displaying a title
*/
function theme_hook_title($content) {
$output .= '<div class="hook_view_title">';
$output .= t('Title:');
$output .= $content;
$output .= '</div>';
return $output;
}
/**
* The implementation of theme_hook_body()
* Link to the style sheet when displaying a body
*/
function theme_hook_body($content) {
$output .= '<div class="hook_view_body">';
$output .= t('Body:');
$output .= $content;
$output .= '</div>';
return $output;
}
/**
* The implementation of theme_hook_hid()
* Link to the style sheet when displaying hid
*/
function theme_hook_hid($content) {
$options = array('1'=>'A', '2'=>'B', '3'=>'C', '4'=>'D', '5'=>'E');
$output .= '<div class="hook_view_value">';
$output .= t('hid value: %value', array('%value' => $options[$content]));
$output .= '</div>';
return $output;
}
?>
hook.css
/* Write the following appropriately */
#hook {
margin-top: 0;
}
#hook img {
margin-bottom: 0.75em;
}
#hook .hook-icon {
float: right;
display: block;
}
#hook .hook-item {
margin-bottom: 1.5em;
}
#hook .hook-item-title {
margin-bottom: 0;
font-size: 1.3em;
}
#hook .hook-item-meta, .hook-item-body {
margin-bottom: 0.5em;
}
#hook .hook-item-categories {
font-size: 0.9em;
}
#hook td {
vertical-align: center;
}
#hook td.categorize-item {
white-space: nowrap;
}
#hook .categorize-item .body {
margin-top: 0;
}
#hook .categorize-item h3 {
margin-bottom: 1em;
margin-top: 0;
}