This article presents the source code for the announcement module we use as our example throughout this series. Each function in the module is described here; most of the functions are implementations of the hook interface for developing modules as described in Part 6, "Building a Custom Module in Drupal." This module is written in Drupal 4.7. The upcoming summary article (Part 15) provides a migration for this module from Drupal version 4.7 to 5.0.
Code listings for the announcement.module
This is the implementation of hook_help. This function allows the announcement module to make documentation available to the Drupal interface. (See http://api.drupal.org/api/HEAD/function/hook_help for more details.)
- Input parameter:
$section - This determines which part of the interface is requesting help content.
Here we return content for the module description under the administration
interface
admin/modules#descriptionand at the top of the form to add a new announcementnode/add#announcement. - Return value
- A string containing the help content
Listing 1. Implementation of hook_help
function announcement_help($section) {
switch ($section) {
case 'admin/modules#description':
return t('Enables the creation of announcement pages ' .
'that are presented on the home page.');
case 'node/add#announcement':
return t('An Announcement. Use this page to add an announcement page.');
}
}
|
This is the implementation of hook_perm. This function supplies the permissions that the announcement module defines. These can then be selected on the Drupal user permissions administration page and used to restrict access to actions the announcement module performs. (See http://api.drupal.org/api/HEAD/function/hook_perm for more details.)
- Return value
- An array of strings used to identify permissible actions
Listing 2. Implementation of hook_perm
function announcement_perm() {
return array('create announcement', 'edit announcement');
}
|
This function is required for modules to define one or more node types. It allows Drupal to determine the names and the attributes of the announcement module node type. (For more details see http://api.drupal.org/api/HEAD/function/hook_node_info.)
- Return value
- An array of information on the module's node types. The minimum required information that needs to be returned is the module's human readable name, the Drupal module name used to build hook function names, and short description of the node type.
Listing 3. Implementation of hook_node_info
function announcement_node_info() {
return array('announcement' => array(
'name' => 'Announcement',
'description' => 'An Announcement. Use this page to add an announcement page.',
'module' => 'announcement'));
}
|
This is the implementation of hook_access. This function allows the announcement module to limit access to the node type it defines depending on the operation currently being performed and the permissions defined in the Drupal user permissions administration page. (See http://api.drupal.org/api/HEAD/function/hook_access for more details.)
- Input parameter:
$op - The Drupal operation currently being performed. Here we want to control access for the 'create', 'view', 'update', and 'delete' operations.
- Input parameter:
$node - The node object on which this operation is being performed
- Return value
- A boolean value depending on whether the operation can be performed or not
Listing 4. Implementation of hook_access
function announcement_access($op, $node) {
global $user;
if ($op == 'create') {
return user_access('create announcement');
}
else if ($op == 'view') {
return user_access('access content');
}
else if ($op == 'update' || $op == 'delete') {
if($user->uid == $node->uid || user_access('edit announcement')) {
return true;
}
else {
return false;
}
}
else {
return false;
}
}
|
This is the implementation of hook_menu. This function allows the announcement module to register URL paths and determine how these requests are to be handled. Depending on the registration, a link may be placed in a menu or as a tab at the top of the page. (See http://api.drupal.org/api/HEAD/function/hook_menu for more details.)
Note: For version 5.X, instead of using the hook_settings function to generate a forum under the admin UI, you need register the URL path, admin/settings/announcement, to map to a callback function that creates this a form.
- Input parameter:
$may_cache - This is a boolean value used to determine whether or not a registered URL path should be cached. Usually, those URL paths that include some dynamic value should not be cached.
- Return value
- An array of registered URL path objects. These contain at least the registered URL path, a string of text used as a title for these paths, an access flag built by testing the access list, a type to determine how this registration should be used, and the name of the callback function that should be called when this URL path is requested.
Listing 5. Implementation of hook_menu
function announcement_menu($may_cache) {
$items = array();
if ($may_cache) {
$items[] = array('path' => 'announcements/add',
'title' => t('Add a new Announcement'),
'access' => node_access('create', 'announcement'),
'type' => MENU_CALLBACK,
'callback arguments' => array('announcement'),
'callback' => 'node_add');
$items[] = array('path' => 'announcements',
'title' => t('Announcements'),
'access' => user_access('access content'),
'type' => MENU_CALLBACK,
'callback' => 'announcement_all');
}
else {
$items[] = array('path' => 'announcements/pager',
'title' => t('Announcements Pager Example'),
'access' => user_access('administer site'),
'type' => MENU_CALLBACK,
'callback' => 'announcement_pager');
if(is_numeric(arg(1))) {
$node = node_load(arg(1));
$items[] = array('path' => 'announcements/' . arg(1),
'title' => t('View an Announcement'),
'access' => node_access('view', $node),
'type' => MENU_CALLBACK,
'callback' => 'node_page');
$items[] = array('path' => 'announcements/' . arg(1) . '/view',
'title' => t('View an Announcement'),
'access' => node_access('view', $node),
'type' => MENU_CALLBACK,
'callback' => 'node_page');
$items[] = array('path' => 'announcements/' . arg(1) . '/edit',
'title' => t('Edit an Announcement'),
'access' => node_access('edit', $node),
'type' => MENU_CALLBACK,
'callback' => 'node_page');
$items[] = array('path' => 'announcements/' . arg(1) . '/delete',
'access' => node_access('delete', $node),
'type' => MENU_CALLBACK,
'callback' => 'node_delete_confirm');
}
}
return $items;
}
|
This function builds a themed set of links, known as the pager, to each page of a paginated list of announcements. This is usually placed at the bottom of each of these paginated pages.
- Return value
- A string of XHTML used to render the pager
Listing 6. Implementation of announcement_pager
function announcement_pager() {
$result = pager_query(db_rewrite_sql("SELECT n.nid, n.created FROM {node} n " .
"WHERE type = 'announcement' ORDER BY n.created DESC"),
variable_get('default_nodes_main', 10));
while ($announcement = db_fetch_object($result)) {
$output .= node_view(node_load($announcement->nid), 1);
}
$output .= theme('pager', NULL, variable_get('default_nodes_main', 10));
return $output;
}
|
This is the implementation of hook_cron. This function allows the announcement module to insert its own actions when the cron.php script is run, usually by the system cron system. This is useful when performing periodic asynchronous tasks (as in this case) such as checking to see if any announcements have expired. We use this hook to mark expired announcements as unpublished so that nonadministrative users cannot see them on the site through standard node mechanisms. (See http://api.drupal.org/api/HEAD/function/hook_cron for more details.)
Listing 7. Implementation of hook_cron
function announcement_cron() {
$queryResult = db_query("UPDATE {node} AS n INNER JOIN {announcement} AS a " .
"ON n.nid = a.nid SET n.status = 0 " .
"WHERE n.type='announcement' AND n.status = 1 AND " .
"a.expiration_date < %d", time());
}
|
This is the implementation of hook_link. This function allows the announcement module to insert its own links into certain parts of Drupal-generated content. In this case Add, Edit, and Delete links are added to Drupal-rendered announcements, depending on the access permissions of the current user. (See http://api.drupal.org/api/HEAD/function/hook_link for more details.)
- Input parameter:
$type - A string identifying the type of link being requested. In this case, the links placed below an announcement node.
- Input parameter:
$node - The node object currently being requested.
- Input parameter:
$teaser - A boolean indicating whether the full announcement is being displayed or just its teaser.
- Return value
- An array of link objects as created by the l() function.
Listing 8. Implementation of hook_link
function announcement_link($type, $node = NULL, $teaser = FALSE) {
global $user;
$links = array();
if($type == 'node' && $node->type == 'announcement') {
if(node_access('create', 'announcement')) {
$links[] = l(t('Add'), "node/add/announcement",
array('title' => t('Add a new announcement')));
}
if(node_access('update', $node)) {
$links[] = l(t('Edit'), "announcements/$node->nid/edit",
array('title' => t('Edit Announcement ') . $node->title));
$links[] = l(t('Delete'), "announcements/$node->nid/delete",
array('title' => t('Delete Announcement ') . $node->title));
}
}
return $links;
}
|
This is the implementation of hook_settings. This function provides an administrative interface for controlling various settings for the announcement module. (See http://api.drupal.org/api/4.7/function/hook_settings for more details.)
Note: This hook is not used in Drupal V5. (http://drupal.org/node/64279#hook-settings)
- Return value
- An array description, in the Drupal Forms API format, of the elements to render the settings interface.
Listing 9. Implementation of hook_settings
function announcement_settings() {
$form = array();
$form['announcement_block_max_list_count'] = array('#type' => 'textfield',
'#title' => t('Maximum number of block announcements'),
'#default_value' => variable_get('announcement_block_max_list_count', 3),
'#description' => t('The maximum number of items listed in the announcement block'),
'#required' => FALSE,
'#weight' => 0
);
$form['announcement_display_classification'] = array('#type' => 'checkbox',
'#title' => t('Display additional announcement classification'),
'#default_value' => variable_get('announcement_display_classification', 1),
'#description' => t('Insert the additional classification in the announcement modules'),
'#required' => FALSE,
'#weight' => 0
);
return $form;
}
|
This is the implementation of hook_form. This function is called to create the form that is displayed when one creates or edits an announcement. (See http://api.drupal.org/api/HEAD/function/hook_form for more details.)
- Input parameter:
$node - The node that is being created or edited
- Return value
- An array description, in the Drupal Forms API format, of the elements to render announcement form
Listing 10. Implementation of hook_form
function announcement_form(&$node) {
if ($node->expiration_date == NULL) {
$node->expiration_date = time() + (365 * 86400);
}
if ($node->publish_date == NULL) {
$node->publish_date = time();
}
$form['title'] = array('#type' => 'textfield',
'#title' => t('Title'),
'#default_value' => $node->title,
'#description' => t('Title of the announcement'),
'#required' => TRUE,
'#weight' => 1
);
$form['publication'] = array('#type'=> 'fieldset',
'#collapsible' => FALSE,
'#title' => t('Publication dates'),
'#weight' => 5
);
$form['publication']['publish_date'] = array(
'#prefix' => '<div class="date_widget">',
'#suffix' => '</div>',
'#type' => 'date',
'#title' => t('Publication date'),
'#default_value' => _announcement_unixtime2drupaldate($node->publish_date)
);
$form['publication']['expiration_date'] = array(
'#prefix' => '<div class="date_widget">',
'#suffix' => '</div>',
'#type' => 'date',
'#title' => t('Expiration date'),
'#default_value' => _announcement_unixtime2drupaldate($node->expiration_date)
);
$form['abstract'] = array('#type' => 'textarea',
'#title' => t('Abstract'),
'#default_value' => $node->abstract,
'#rows' => 3,
'#description' => t('Short summary of the full announcement'),
'#required' => TRUE,
'#weight' => 9
);
$form['body'] = array('#type' => 'textarea',
'#title' => t('Body'),
'#default_value' => $node->body,
'#description' => t('Full content for the announcement which is shown ' .
'with the abstract on the details page'),
'#required' => TRUE,
'#weight' => 10
);
return $form;
}
|
This function creates the themed list of announcements. For the average user, only those announcements that have not expired are shown. If the user has access to edit announcements, then all are shown.
- Return value
- Formatted content showing all announcements
Listing 11. Function to show all announcements
function announcement_all() {
if (user_access('edit announcement')) {
$queryResult = db_query(
"SELECT n.nid FROM node n INNER JOIN announcement a ON n.nid = a.nid " .
"WHERE n.type='announcement' ORDER BY n.sticky DESC, A.publish_date DESC");
}
else {
$queryResult = db_query(
"SELECT n.nid FROM node n INNER JOIN announcement a ON n.nid = a.nid " .
"WHERE n.type='announcement' AND a.expiration_date > %d " .
"ORDER BY n.sticky DESC, a.publish_date DESC", date("U"));
}
$page_content = array();
$page_content[] = "<h2 class='category'>Announcements</h2>";
while ($announcement = db_fetch_object($queryResult)) {
$announcement = node_load($announcement->nid);
$announcement->url = url('announcements/' . $announcement->nid);
$page_content[] = theme('announcement_compact', $announcement);
}
return implode('', $page_content);
}
|
This is the implementation of hook_view. This function allows the announcement module to add extra variables to any announcement nodes before they are rendered by the Drupal system. Here we insert the URL to the announcement detail into the announcement node object. (See http://api.drupal.org/api/HEAD/function/hook_view for more information.
- Input parameter:
$node - The node to be displayed.
- Input parameter:
$teaser - Whether a summary is being generated for display or the whole node.
- Input parameter:
$page - Whether the node is being displayed as a standalone page, i.e., the page title should not be displayed.
- Return value
- The modified node object.
Listing 12. Implementation hook_view
function announcement_view(&$node, $teaser = FALSE, $page = FALSE) {
if ($page) {
$node->url = url('announcements/' . $node->nid);
}
}
|
This is the implementation of hook_validate. This function is called after a user has submitted an creation or edit form. Here we check to ensure that the expiration date is after the publish date. (See http://api.drupal.org/api/HEAD/function/hook_validate for more details.)
- Input parameter:
$node - The node being validated
Listing 13. Implementation of hook_validate
function announcement_validate($node) {
if ($node) {
$publish_date = _announcement_drupaldate2unixtime($node->publish_date);
$expiration_date = _announcement_drupaldate2unixtime($node->expiration_date);
if ($publish_date >= $expiration_date) {
form_set_error('publish_date',
t('The publish date of an announcement must be before its expiration date.'));
}
}
}
|
This is the implementation of hook_submit. This function allows the announcement module to change the node object information prior to saving this data to the database. Here we convert the date values to integers and ensure the announcement is properly classified in the taxonomy. (See http://api.drupal.org/api/HEAD/function/hook_submit for more details.)
- Input parameter:
$node - The node being submitted to the database
Listing 14. Implementation of the hook_submit
function announcement_submit(&$node) {
$node->publish_date = _announcement_drupaldate2unixtime($node->publish_date);
$node->expiration_date = _announcement_drupaldate2unixtime($node->expiration_date);
$now = time();
if($now > $node->publish_date && $now < $node->expiration_date) {
$node->status = 1;
}
else {
$node->status = 0;
}
$vocab = announcement_get_vocabulary_by_name('IBC');
$term = taxonomy_get_term_by_name('announcements');
$node->taxonomy = _announcement_merge_tid($vocab->vid,
$term[0]->tid,
$node->taxonomy);
}
|
announcement_get_vocabulary_by_name
This helper function returns the vocabulary object given the vocabulary name.
- Input parameter:
$name - Name of the vocabulary to retrieve
- Return value
- Vocabulary object
Listing 15. Find a vocabulary by its name
function announcement_get_vocabulary_by_name ($name) {
$results = db_query('SELECT * FROM {vocabulary} WHERE name = "%s"', $name);
if (db_num_rows($results) > 0) {
return db_fetch_object($results);
}
else {
return null;
}
}
|
This local function, identified as such by the leading underscore in the function name, merges terms in a vocabulary
- Input parameter:
$vid - Vocabulary id
- Input parameter:
$tid - Term id
- Input parameter:
$taxonomy - Taxonomy in which the vocabulary exists
- Return value
- The updated taxonomy
Listing 16. Merge vocabulary terms
function _announcement_merge_tid($vid, $tid, $taxonomy) {
$values = array_values($taxonomy[$vid]); // get all tid values for vocabulary
if (!in_array($tid, $values)) { // if the tid is not there
$taxonomy[] = $tid; // add it
}
return $taxonomy;
}
|
This is the implementation of hook_load. This function allows the announcement module to add extra data to the announcement node object. Here we merging the associated date from the announcement table. (See http://api.drupal.org/api/HEAD/function/hook_load for more information.)
- Input parameter:
$node - The node being loaded from the database
- Return value
- Return any additional values from the database that will be merged into the node
Listing 17. Implementation of hook_load
function announcement_load(&$node) {
$additions = db_fetch_object(db_query('SELECT * FROM {announcement} ' .
'WHERE nid = %d', $node->nid));
return $additions;
}
|
This is the implementation of hook_insert. This function allows the announcement module to take action when a new node is being inserted in the database. Here we are inserting a new record into the announcement table using data found in the node object. (See http://api.drupal.org/api/HEAD/function/hook_insert for more details.)
- Input parameter:
$node - Node being inserted into the database
Listing 18. Implementation of hook_insert
function announcement_insert($node) {
db_query("INSERT INTO {announcement} (nid, abstract, publish_date, expiration_date) " .
"VALUES (%d, '%s', '%d', '%d')", $node->nid, $node->abstract,
$node->publish_date, $node->expiration_date);
}
|
This is the implementation of hook_update. This function allows the announcement module to take action when an existing announcement node is being updated in the database. Here we perform our own database updates, such as putting information into the announcement table. (See http://api.drupal.org/api/HEAD/function/hook_update for more information.)
- Input parameter:
$node - The node being updated in the database
Listing 19. Implementation of hook_update
function announcement_update($node) {
db_query("UPDATE {announcement} SET abstract='%s', publish_date = '%s', " .
"expiration_date = '%s' WHERE nid = %d", $node->abstract,
$node->publish_date, $node->expiration_date, $node->nid);
}
|
This is the implementation of hook_delete. This function allows the announcement module to take action when an announcement node is being deleted from the database. (See http://api.drupal.org/api/HEAD/function/hook_delete for more information.)
- Input parameter:
$node - The node being deleted from the database
Listing 20. Implementation of hook_delete
function announcement_delete($node) {
db_query('DELETE FROM {announcement} WHERE nid = %d', $node->nid);
}
|
This is the implementation of hook_block. This function defines two blocks that are provided to the Drupal system. The site administrator can then use the Drupal interface to chose where to put these on the page if needed. The first block lists the recently updated announcements and the second uses the IBC vocabulary, described in Part 11, "Using taxonomies in Drupal," to list announcements by categories. (See http://api.drupal.org/api/HEAD/function/hook_block for more details.)
- Input parameter:
$op - The kind of information to retrieve about the announcement block.
- Input parameter:
$delta - Number of the block to return.
- Input parameter:
$edit - If
$opis 'save', this variable contains the submitted block configuration form data. - Return value
- If $op is 'list', it returns an array of arrays, each of which must define an info element describing the block.
If $op is 'view', it returns an array which defines 'subject' and 'content' elements defining the block indexed by
$delta.
Listing 21. Implementation of hook_block
function announcement_block($op = 'list', $delta = 0, $edit = array()) {
global $user;
if ($op == 'list') {
$blocks[0]['info'] = t('Recently updated announcements');
$blocks[1]["info"] = t('List nodes in the IBC vocabulary');
return $blocks;
}
else if ($op == 'view') {
$block = array();
switch ($delta) {
case 0:
$announcement_items = array();
if (user_access('access content')) {
$q = 'SELECT N.uid,N.nid,N.title,A.publish_date,N.status '.
'FROM {node} N JOIN {announcement} A USING(nid) '.
"WHERE N.type='announcement' ".
'AND N.status = 1 '.
'AND A.publish_date < %d ' .
'AND A.expiration_date > %d ' .
'ORDER BY A.publish_date DESC ';
$now = time();
$items = variable_get('announcement_block_max_list_count', 3);
$announcements = db_query_range($q, $now, $now, 0, $items);
while (db_num_rows($announcements) > 0 and
$announcement = db_fetch_object($announcements)) {
$announcement_items[] = $announcement;
}
}
$block['subject'] = t('Announcements');
$block['content'] = theme('announcement_block_list',
$announcement_items);
break;
case 1:
if (user_access("access content")) {
$vocabulary = announcement_get_vocabulary_by_name('IBC');
$block["subject"]= t('IBC');
$block["content"]= announcement_vocab_vert($vocabulary->vid);
}
}
return $block;
}
}
|
This function was adapted from the taxonomy_dhtml module to build the content to be displayed in a block of nodes classified in the vocabulary.
It builds an array which holds all children of current term. It is necessary to build a proper or value in the HREF.
- Input parameter:
$vocabulary_id - ID of the vocabulary
- Input parameter:
$op - An operation that can be added to the URL of a node listed
- Return value
- Output for the display of the block
Listing 22. Displaying nodes classified by vocabulary terms
function announcement_vocab_vert($vocabulary_id, $op = NULL) {
$tree = taxonomy_get_tree($vocabulary_id);
foreach ($tree as $term) {
$url = "taxonomy/term/$term->tid";
if ($op) {
$url .= "/$op";
}
$link = l(t($term->name), $url, array("title" => t($term->description)));
$out .= _taxonomy_depth($term->depth, " ")."- $link";
$count = taxonomy_term_count_nodes($term->tid);
if ($count) {
$out .= " ($count)";
$out .= _announcement_by_terms($term->tid);
}
else {
$out .= " (0)";
}
$out .= "<br/>";
}
return $out;
}
|
This local function returns a themed list of link to announcements for a given vocabulary term.
- Input parameter:
$tid - The ID of the term to be rendered
- Return value
- The rendered term for output
Listing 23. Output the display of a term
function _announcement_by_terms($tid) {
$result = '';
$tids = array( $tid );
$nodes = taxonomy_select_nodes($tids, 'or', 0, FALSE);
while ($r = db_fetch_object($nodes)) {
$url = "announcements/". $r->nid;
$result .= " |
This is the implementation of hook_nodeapi. This function allows the announcement module to take action when Drupal recognizes a particular action is being performed. Here, if the current Drupal operation is "update index", we add the abstract of this announcement to the search index. (See http://api.drupal.org/api/HEAD/function/hook_nodeapi for more information.)
- Input parameter:
$node - The node being processed
- Input parameter:
$op - The action on the node, such as "delete", "update index"
- Return value
- This is dependent on the operation, but in our case we return the text that needs to be indexed for this node.
Listing 24. Implementation of hook_nodeapi
function announcement_nodeapi(&$node, $op) {
switch ($op) {
case 'update index':
if ($node->type == 'announcement') {
$text = '';
$q = db_query(
'SELECT a.abstract FROM node n LEFT JOIN announcement a ON '.
'n.nid = a.nid WHERE n.nid = %d', $node->nid);
if ($r = db_fetch_object($q)) {
$text = $r->abstract;
}
return $text;
}
}
}
|
The following functions provide a method by which a theme designer can override how data generated by this module is presented. There are two classes of theme functions:
- theme_announcement
- Generic theme function that will work for all themes and theme engines
- phptemplate_announcement
- Theme function that will be used by the phptemplate engine
For the sake of these articles we did not insert any code to structure data into the default theme functions. Instead we concentrated on showing how the overriding phptemplate theme functions structure the data. Usually, these phptemplate functions are placed within your theme directory's phptemplate.php file.
Listing 25. Theme functions
function theme_announcement($announcement) {
return '';
}
function theme_announcement_compact($announcement) {
return '';
}
function theme_announcement_block_list($announcement_list) {
return '';
}
function phptemplate_announcement($announcement) {
return _theme_phptemplate_announcement($announcement, 'announcement');
}
function phptemplate_announcement_compact($announcement) {
return _theme_phptemplate_announcement
($announcement, 'announcement_compact');
}
function phptemplate_announcement_block_list($announcement_list) {
global $user;
return _phptemplate_callback('announcement_block_list',
array('announcements' => $announcement_list,
'user'=> $user));
}
function _theme_phptemplate_announcement
($announcement, $announcement_template) {
$expired = FALSE;
if ($announcement->expiration_date < time()) {
$expired = TRUE;
}
$variables = array(
'title' => $announcement->title,
'body' => $announcement->body,
'links' => $announcement->links ? theme('links', $announcement->links) : '',
'abstract' => $announcement->abstract,
'published' => format_date($announcement->publish_date,'custom','j M, Y'),
'expires' => format_date($announcement->expiration_date,'custom','j M, Y'),
'expired' => $expired,
'node' => $announcement
);
return _phptemplate_callback($announcement_template, $variables);
}
|
_announcement_drupaldate2unixtime
This is a local utility function for converting a structured data array containing a date into an integer representation.
- Input parameter:
$drupal_date - A representation of a date in an array indexed by day, month, and year
- Return value
- An integer representation of the date
Listing 26. Converting dates from Drupal internal representation to an integer
function _announcement_drupaldate2unixtime($drupal_date) {
$year = $drupal_date["year"];
$month = $drupal_date["month"];
$day = $drupal_date["day"];
return mktime(0,0,0, (int)$month, (int)$day, (int)$year);
}
|
_announcement_unixtime2drupaldate
This is a local utility function for converting a integer representation of the date to a structured data array.
- Input parameter:
$unixtime - An integer representation of the date
- Return value
- A representation of a date in an array indexed by day, month, and year
Listing 27. Converting dates from an integer to a Drupal internal representation
function _announcement_unixtime2drupaldate($unixtime) {
return array('day' => format_date($unixtime, 'custom', 'j'),
'month' => format_date($unixtime, 'custom', 'n'),
'year' => format_date($unixtime, 'custom', 'Y'));
}
|
This article presents the announcement module described in our series. As well as providing the actual file as a download, each function is described and references to more information about the Drupal hook functions used are provided. We hope this article answers some of the questions we've been asked regarding the announcement module source.
The next article wraps up the series with a recap of lessons learned and a look forward, discussing where Drupal is going with version 5.
| Description | Name | Size | Download method |
|---|---|---|---|
| announcement module source code | announcement.module | 24KB | HTTP |
Information about download methods
Learn
-
Eclipse: Read all about this open development platform.
-
Eclipse PHP debugger: Learn more about this project.
-
HTMLtidy: Read about maintenance and further development of the HTML Tidy program.
-
phpDocumentor: The current standard auto-documentation tool for the php language.
-
Visit developerWorks Architecture to get the resources you need to advance your skills in the IT architecture arena.
- Stay current with developerWorks technical events and webcasts.
-
Browse the technology bookstore for books on these and other technical topics.
-
RSS feed for this series. (Find out more about RSS.)
Get products and technologies
Get products and technologies
-
Download Zend debugger
- Build your
next development project with IBM trial software, available for download directly from
developerWorks.
Discuss
- Participate in the discussion forum.
- Participate in developerWorks
blogs and get involved in the developerWorks community.

Alister Lewis-Bowen is a senior software engineer in IBM's Internet Technology Group. He has worked on Internet and Web technologies as an IBM UK employee since 1993. Alister was brought to the U.S. to work on the Web sites for the IBM-sponsored sports events, then as senior Webmaster for ibm.com. He is currently helping create semantic Web prototypes.

Louis Weitzman is a senior software engineer in IBM's Internet Technology Group. For 30 years he has worked at the intersection of design and computation. He helped develop an XML, fragment-based content management system in use by ibm.com, and currently is involved with bringing the design process to emerging projects.


