Packt Publishing Community Experience, Distilled

Creating Our First Module using Drupal 6 (Part1)

HomeBooksSupportFreeAuthorsAward
WELCOME YOUR ACCOUNT NEWSLETTERS ARTICLES ABOUT US

 
Article Network FAQ

Want to know more about Packt's Article Network? Interested in contributing your article ideas?

Please visit our FAQ for more information.


See More

SEARCH

Search our Site


Theming Modules in Drupal 6

The Philosophy Quotes module that we will create in this article will use Drupal's theme system and a simple database query to theme the content of a custom content type.

Here are some of the items we will cover in this article by Matt Butcher while working on this module:

  • Creating a custom content type
  • Performing simple database operations
  • Registering a module's theme functions with the hook_theme() function
  • Adding theme hooks to a module
  • Adding CSS stylesheets to a module's default theme
  • Using theme CSS and template files to override default module theme functions

See More
 
Creating Our First Module using Drupal 6 (Part1)

In this two-part article series by Matt Butcher, we will create our first Drupal module. This article assumes basic knowledge of Drupal module development. Our first module will make use of an existing web service to pull in some XML data, format it, and display it as a block in the site's layout.

We will cover the following topics in this article:

  • Creating the .info and .module files
  • Creating a new module
  • Using basic hooks
  • Installing and configuring the module
  • Using important Drupal functions

Starting Out

Our first module is going to fetch XML data from Goodreads, a free social networking site for avid readers. There, users track the books they are reading and have read, rate books and write reviews, and share their reading lists with friends.

Reading lists at Goodreads are stored in bookshelves. These bookshelves are accessible over a web-based XML/RSS API. We will use that API to display a reading list on the Philosopher Bios website(example website)

To integrate the Goodreads information in Drupal, we will create a small module. Since this is our first module, we will get into greater details.

A Place for the Module

In Drupal, every module is contained in its own directory. This simplifies organization; all of the module's files are located in one place.

To keep naming consistent throughout the module (a standard in Drupal), we will name our directory with the module name. Later, we will install this module in Drupal, but for development, the module directory can be wherever it is most convenient.

Once we have created a directory named goodreads, we can start creating files for our module. The first file we need to create is the .info (dot-info) file.

Creating a .info File

Before we start coding our new module, we need to create a simple text file that will hold some basic information about our module. Various Drupal components use the information in this file for module management.

The .info file is written as a PHP INI file, which is a simple configuration file format.

If you are interested in the details of INI file processing, you can visit http://php.net/manual/en/function.parse-ini-file.php for a description of this format and how it can be parsed in PHP.

Our .info file will only be five lines long, which is probably about average.

The .info file must follow the standard naming conventions for modules. It must be named <modulename>.info, where <modulename> is the same as the directory name. Our file, then, will be called goodreads.info.

Following are the contents of goodreads.info:

;$Id$
name = "Goodreads Bookshelf"
description = "Displays items from a Goodreads Bookshelf"
core = 6.x
php = 5.1

This file isn't particularly daunting. The first line of the file is, at first glance, the most cryptic. However, its function is mundane: it is a placeholder for Drupal's CVS server.

Drupal, along with its modules, is maintained on a central CVS (Concurrent Version System) server. CVS is a version control system. It tracks revisions to code over time. One of its features is its ability to dynamically insert version information into a file. However, it needs to know where to insert the information. The placeholder for this is the special string $Id$. But since this string isn't actually a directive in the .info file, it is commented out with the PHP INI comment character, ; (semi-colon).

You can insert comments anywhere in your .info file by beginning a line with the ; character.

The next four directives each provide module information to Drupal.

The name directive provides a human-readable display name for the module. Here's an example:

In this above screenshot, the names Aggregator and Blog are taken from the values of the name directives in these modules' .info files.

While making the module's proper name short and concise is good (as we did when naming the module directory goodreads above), the display name should be helpful to the user. That usually means that it should be a little longer, and a little more descriptive.

However, there is no need to jam all of the module information into the name directive. The description directive is a good place for providing a sentence or two describing the module's function and capabilities.

The third directive is the core directive.

The core and php directives are new in Drupal 6.

This directive specifies what version of Drupal is required for this module to function properly. Our value, 6.x, indicates that this module will run on Drupal 6 (including its minor revisions). In many cases, the Drupal packager will be able to automatically set this (correctly). But Drupal developers are suggesting that this directive be set manually for those who work from CVS.

Finally, the php directive makes it possible to specify a minimum version number requirement for PHP. PHP 5, for example, has many features that are missing in PHP 4 (and the modules in this book make use of such features). For that reason, we explicitly note that our modules require at least PHP version 5.1.

That's all there is to our first module .info file. What we have here is sufficient for our Goodreads module.

Now, we are ready to write some PHP code.

A Basic .module File

There are two files that every module must have (though many modules have more). The first, the .info file, we examined above. The second file is the .module (dot-module) file, which is a PHP script file. This file typically implements a handful of hook functions that Drupal will call at pre-determined times during a request.    

Here, we will create a .module file that will display a small formatted section of information. Later in this article, we will configure Drupal to display this information to site visitors.

Our Goal: A Block Hook

For our very first module, we will implement the hook_block() function. In Drupal parlance, a block is a chunk of auxiliary information that is displayed on a page alongside the main page content. Sounds confusing? An example might help.

Think of your favorite news website. On a typical article page, the text of the article is displayed in the middle of the page. But on the left and right sides of the page and perhaps at the top and bottom as well, there are other bits of information: a site menu, a list of links to related articles, links to comments or forums about this article, etc. In Drupal, these extra pieces are treated as blocks.

The hook_block() function isn't just for displaying block contents, though. In fact, this function is responsible for displaying the block and providing all the administration and auxiliary functions related to this block. Don't worry... we'll start out simply and build up from there.

Starting the .module

Drupal follows rigorous coding and documentation standards (http://drupal.org/coding-standards). In this article, we will do our best to follow these standards. So as we start out our module, the first thing we are going to do is provide some API documentation.

Just as with the .info file, the .module file should be named after the module. Following is the beginning of our goodreads.module file:

<?php
// $Id$
/**
* @file
* Module for fetching data from Goodreads.com.
* This module provides block content retrieved from a
* Goodreads.com bookshelf.
* @see http://www.goodreads.com
*/

The .module file is just a standard PHP file. So the first line is the opening of the PHP processing instruction: <?php. Throughout this article you may notice something. While all of our PHP libraries begin with the <?php opening, none of them end with the closing ?> characters.

This is intentional, in fact, it is not just intentional, but conventional for Drupal. As much as it might offend your well-formed markup language sensibilities, it is good coding practice to omit the closing characters for a library.

Why? Because it avoids printing whitespace characters in the script's output, and that can be very important in some cases. For example, if whitespace characters are output before HTTP headers are sent, the client will see ugly error messages at the top of the page.

After the PHP tag is the keyword for the version control system:

// $Id$

When the module is checked into the Drupal CVS, information about the current revision is placed here.

The third part of this example is the API documentation. API documentation is contained in a special comment block, which begins /** and ends with a */. Everything between these is treated as documentation. Special extraction programs like Doxygen can pull out this information and create user-friendly programming information.

The Drupal API reference is generated from the API comments located in Drupal's source code. The program, Doxygen, (http://www.stack.nl/~dimitri/doxygen/) is used to generate the API documents from the comments in the code.

The majority of the content in these documentation blocks (docblocks, for short) is simply text. But there are a few additions to the text.

First, there are special identifiers that provide the documentation generating program with additional information. These are typically prefixed with an @ sign.

/**
* @file
* Module for fetching data from Goodreads.com.
* This module provides block content retrieved from a
* Goodreads.com bookshelf.
* @see http://www.goodreads.com
*/

In the above example, there are two such identifiers. The @file identifier tells the documentation processor that this comment describes the entire file, not a particular function or variable inside the file. The first comment in every Drupal PHP file should, by convention, be a file-level comment.

The other identifier in the above example is the @see keyword. This instructs the documentation processor to attempt to link this file to some other piece of information. In this case, that piece of information is a URL. Functions, constants, and variables can also be referents of a @see identifier. In these cases, the documentation processor will link this docblock to the API information for that function, constant, or variable.

With these formalities out of the way, we're ready to start coding our module.



Learning Drupal 6 Module Development
 
Learning Drupal 6 Module Development
  • A practical tutorial for creating your first Drupal 6 modules with PHP
  • Specifically written for Drupal 6 development
  • Program your own Drupal modules
  • No experience of Drupal development required
  • Know Drupal 5? Learn what's new in Drupal 6
  • Integrate AJAX functionality with the jQuery library
  • Packt donates a percentage of every book sold to the Drupal foundation
 http://www.packtpub.com/drupal-6-module-development/book




The hook_block() Implementation

Our module will display information inside a Drupal block. To do this, we need to implement the hook_block() function.

Remember, what we are doing here is providing a function that Drupal will call. When Drupal calls a hook_block() function, Drupal passes it as many as three parameters:

  • $op
  • $delta
  • $edit

The $op parameter will contain information about the type of operation Drupal expects the module to perform. This single hook implementation is expected to be able to perform a variety of different operations. Is the module to output basic information about itself? Or display the block? Or provide some administration information? The value of $op will determine this.

$op can have the following four possible values:

  • list: This is passed when the module should provide information about itself. For example, when the list of modules is displayed in the module administration screen, the $op parameter is set to list.
  • view: This value is passed in $op when Drupal expects the block hook to provide content for displaying to the user.
  • configure: This value is passed when Drupal expects an administration form used to configure the block. We will look at this later.
  • save: This value is passed when configuration information from the form data generated by configure needs to be saved.

The $delta parameter is set during a particular operation. When $op is set to the string view, which is the operation for displaying the block, then the $delta will also be set. $delta contains extra information about what content should be displayed.

Using deltas, you can define a single hook_block() function that can display several different blocks. For example, we might define two deltas—one that displays our Goodreads bookshelf, and the other that displays information about our Goodreads account. Which one is displayed will depend on which $delta value is passed into the goodreads_block() function.

Finally, the $edit parameter is used during configuration (when the save operation is called). Since we are not implementing that operation in our first module, we will not use this parameter.

Drupal is meticulously documented, and the API documents are available online at http://api.drupal.org. More information about hook_block() parameters is available at this URL: http://api.drupal.org/api/function/hook_block/6.

All hook methods should follow the module naming convention: <module name>_<hook name>. So our goodreads block hook will be named goodreads_block().

/**
* Implementation of hook_block()
*/
function goodreads_block($op='list', $delta=0, $edit=array()) {
switch ($op) {
case 'list':
$blocks[0]['info'] = t('Goodreads Bookshelf');
return $blocks;
case 'view':
$blocks['subject'] = t('On the Bookshelf');
$blocks['content'] = t('Temporary content');
return $blocks;
}
}

Following Drupal conventions, we precede the function with a documentation block. For hooks, it is customary for the documentation therein to indicate which hook it is implementing.

Next is our function signature: function goodreads_block($op='list', $delta=0, $edit=array()). The $op, $delta, and $edit parameters are all explained above. Each parameter is initialized to a default value. Here, we follow the customary defaults, but you can define them otherwise if you prefer.

As I mentioned earlier the $op parameter might be set to one of several different values.

What we do in this function is largely determined by which of those four values is set in the $op flag. For that reason, the first thing we do in this function is use a switch statement to find out which operation to execute.

Each case in the switch statement handles one of the different operations. For now, we don't have any administration configuration to perform, so there are no cases to handle either configure or save operations. We just need to handle the list and view operations. Let's look at each.

case 'list':
$blocks[0]['info'] = t('Goodreads Bookshelf');
return $blocks;

When Drupal calls this hook with $op set to 'list', then this module will return a two‑dimensional array that looks as follows:

array(
[0]=> (
'info' => 'Goodreads Bookshelf'
))

Each element in this array is a block descriptor, which provides information about what this block implementation does. There should be one entry here for every $delta value that this function recognizes. Our block will only return one value (we don't make use of deltas), so there is only one entry in the block descriptor array.

A block descriptor can contain several different fields in the associative array. One is required: the 'info' field that we have set above. But we could also provide information on caching, default weighting and placement, and so on.

For detailed information on this and other aspects of the hook_block() hook, see the API documentation: http://api.drupal.org/api/function/hook_block/6

Drupal uses the 'info' field to display an item in the module management list, which we will see in the Installing a Module section of this article.

The t() Function

In this example, there is one more thing worthy of mention. We use the function t(). This is the translation function. It is used to provide multi-language support and also provide a standard method of string substitution. When t() is called, Drupal will check to see if the user's preferred language is other than the default (US English). If the user prefers another language, and that language is supported, then Drupal will attempt to translate the string into the user's preferred language.

For multi-language support, you will need to enable the Content translation module.

Whenever we present hard-coded text to a user, we will use the t() function to make sure that multi-language support is maintained.

In simple cases, the t() function takes just a string containing a message. In this case, the entire string will be translated. But sometimes, extra data needs to be passed into the string function. For example, we may want to add a URL into a string dynamically:

'Trying to access !url.'

In this case, we want t() to translate the string, but to substitute a URL in place of the !url placeholder. To do this, we would call t() with the following parameters:

t('Trying to access !url.', array('!url'=>'http://example.com'));

In this example, t() has two arguments: the string to translate, and an associative array where the key is the placeholder name and the value is the value to be substituted. Running the above when the locale is set to English will result in a string as follows:

Trying to access http://example.com

There are three different kinds of placeholder. We have seen one above.

  • !: Placeholders that begin with the exclamation point (!) are substituted into the string exactly as is.

Sometimes it is desirable to do some escaping of the variables before substituting them into the string. The other two placeholder markers indicate that extra escaping is necessary.

  • @: Placeholders that begin with an @ sign will be escaped using the check_plain() function. This will, for example, convert HTML tags to escaped entities. t('Italics tag: @tag', array( '@tag', ''<i>)) will produce the string 'Italics tag: <i>'.
  • %: Placeholders that begin with the percent sign (%) are not only escaped, like those that start with the @, but are also themed. Usually, the result of this theming is that the output value is placed in italics. So t('Replacing %value.', array('%value=>'test') will result in something like 'Replacing <em>test</em>'. The <em></em> tags are added by the translation function.

Don't trust user-entered data
It is always better to err on the side of caution. Do not trust data from external sources (like users or remote sites). When it comes to the t() function, this means you should generally not use placeholders beginning with ! if the source of the string to be substituted is outside of your control. (For example, it is inadvisable to do this: t('Hello !user', array('!user' => $_GET['username']). Using @user or %user is safer.

We will use the t() function throughout this article. For now, though, let's continue looking at the hook_block() function we have created.

A view Operation

Now let's turn to the view case. This second case in our switch statement looks as follows:

case 'view':
$blocks['subject'] = t('On the Bookshelf');
$blocks['content'] = t('Temporary content');
return $blocks;

The view operation should return one block of content for displaying to the end user. This block of content must have two parts stored as name/value pairs in an associative array: a subject and a content item.

The subject is the title of the block, and the content is main content of the block. The value of the subject entry will be used as a title for the block, while the content will be placed into the block's content.

Again, we used the translation function, t(), to translate the title and content of this block.

While it is not terribly exciting yet, our module is ready to test. The next thing to do is install it.



Learning Drupal 6 Module Development
 
Learning Drupal 6 Module Development
  • A practical tutorial for creating your first Drupal 6 modules with PHP
  • Specifically written for Drupal 6 development
  • Program your own Drupal modules
  • No experience of Drupal development required
  • Know Drupal 5? Learn what's new in Drupal 6
  • Integrate AJAX functionality with the jQuery library
  • Packt donates a percentage of every book sold to the Drupal foundation
 http://www.packtpub.com/drupal-6-module-development/book




Installing a Module

We have a working module. Now we need to install it. This is typically done in three steps:

  1. Copying the module to the correct location
  2. Enabling the module
  3. Configuring Drupal to display the module's content

Some of the contributed modules for Drupal require additional setup steps. Such steps are documented by the module's authors.

We will walk through each of these three steps.

Step 1: Copying the Module

Modules in Drupal are stored in one of the three places under Drupal's root directory:

  • modules/: This is the directory for core modules. Only modules supplied as part of the Drupal distribution should be stored here. None of our modules will ever be located here.
  • sites/all/modules/: This is the directory for modules that should be available to all of the sites hosted on this Drupal installation. Usually, this is where you want to put your module.
  • sites/<site name>/modules: Drupal can host multiple sites. Each site has a directory inside the sites/ folder. For example, the default site is located in sites/default/. If you want to install site-specific modules on an instance of Drupal that runs multiple sites, the modules should go into the sites/<site name>/modules/ directory, where <site name> should be replaced by the correct site name.

In this article, we will be storing our modules under the sites/all/modules/ directory.

However, this directory is not created by default, so we will need to create it by hand.

On my Linux server, Drupal is installed in /var/www/drupal/. (Yours may be somewhere else.) All of the file system paths will be relative to this directory. We will add the appropriate subdirectory inside the sites/all/ directory:

In this example, we change into the appropriate directory and create the new modules/ directory.

By default, the permissions on the directory should be set to allow the web-server user (such as www-data) access to the files in the module. However, on some systems you may have to set these yourselves.

On Windows, the same can be done through Windows explorer, and the same goes for Mac and Finder. Simply locate your Drupal installation directory, navigate down to sitesall, and create a new folder named modules.

Next, we need to copy our module into this directory.

UNIX and Linux users: Don't move it; link it!
If you are actively developing a module, sometimes it is more convenient to create a symbolic link to the module directory instead of moving or copying the directory: ln -s /home/mbutcher/modules/goodreads/var/www/drupal/sites/all/modules/goodreads

Now we have our module in a location where Drupal expects to find modules.

Copying the module to the correct location is all we need to do for Drupal to recognize the module, but new modules are disabled by default. We will need to log in to the web interface and enable the module.

Step 2: Enabling the Module

A module is enabled through the Drupal Administration interface. Once logged into Drupal, navigate to Administer | Site Building | Modules in the left-hand navigation.

This page lists all the modules, beginning with the core modules (those installed by default). At the very bottom of this page is the list of third-party modules. Our module will appear in that list.

To activate the module, simply check the box under the Enabled heading, and then click the Save configuration button.

Where did Drupal get the information about our module? For most of this part, this information came from our goodreads.info file.

Next, we need to configure the module to display on our site.

Step 3: Displaying the Module's Content

The module we have created is a block module. Typically, blocks are displayed in specifically defined locations on the screen. What we want to do now is tell Drupal where to display our block content.

Just as with enabling the module, this is done through the administration interface. Go to Administer | Site Building | Blocks to configure block placement.

This tools allows us to configure the details of how blocks appear on the site. In fact, the site uses the templates that a site visitor would see. You can see how the site looks as you configure it.

At the bottom of this page is the block configuration tool—lists of modules along with page placement parameters. We will configure our goodreads module to appear in the right sidebar.

If all goes well, then our goodreads module should display in the right sidebar. Make sure to press the Save Blocks button at the bottom. Otherwise the block's new location will not be saved.

To generate the preceding screen, the block placement screen calls the hook_block() functions for each of the block modules, setting $op to list.

When the block's new location is saved, it will be displayed in the right-hand column on all of our pages.

What is the content in this module? What we see in the above screenshot are the fields returned when Drupal's module manager calls the hook_block() function of our module with something equivalent to this:

goodreads_block('view');

This will return the $blocks array, whose contents look like this:

array(
'subject' => 'On the Bookshelf',
'content' => 'Temporary content'
)

The subject value is used as the block's title, and the content item is used as the block's content.

Our module is installed. But it is doing very little. Next, we will add some sophistication to our module.

Summary

So far, we have created a basic module that uses hook_block() to add block content and installed this basic module. As it stands, however, this module does no more than simply displaying a few lines of static text.

In the next part, we are going to extend the module's functionality. We will add a few new functions that retrieve and format data from Goodreads.




If you have read this article you may be interested to view :



Learning Drupal 6 Module Development
 
Learning Drupal 6 Module Development
  • A practical tutorial for creating your first Drupal 6 modules with PHP
  • Specifically written for Drupal 6 development
  • Program your own Drupal modules
  • No experience of Drupal development required
  • Know Drupal 5? Learn what's new in Drupal 6
  • Integrate AJAX functionality with the jQuery library
  • Packt donates a percentage of every book sold to the Drupal foundation
 http://www.packtpub.com/drupal-6-module-development/book


About the Author

Matt Butcher is the principal consultant for Aleph-Null, Inc. (http://aleph-null.tv), where he specializes in content management systems, Linux system integration, and Open Source technologies. He has been an active participant in Open Source technologies for over a decade. Along with Learning Drupal 6, Matt has also written Mastering OpenLDAP, Managing and Customizing OpenCms 6, and Building Websites with OpenCms, all of which are published by Packt. Matt writes his technical blog at technosophos.com. When not pushing bits, Matt likes to explore Chicago with his wife and three daughters.


Books from Packt

Flash with Drupal
Flash with Drupal

Drupal 6 JavaScript and jQuery
Drupal 6 JavaScript and jQuery

Learning jQuery 1.3
Learning jQuery 1.3

Spring Web Flow 2 Web Development
Spring Web Flow 2 Web Development

Joomla! E-Commerce with VirtueMart
Joomla! E-Commerce with VirtueMart

Django 1.0 Website Development
Django 1.0 Website Development

Drupal 6 Site Builder Solutions
Drupal 6 Site Builder Solutions

WordPress Plugin Development: Beginner's Guide
WordPress Plugin Development: Beginner's Guide








 
Article Network


Packt Article Network

Visit Packt's Article Network, for all the latest quality, relevant and free content.
See More


NEWSLETTER

Sign up for updates, offers, free downloads and you could win an iPod Shuffle.
Subscription center


Creating Our First Module using Drupal 6 (Part2)

In the first part of this 2-part article series we had created a basic module that uses hook_block() to add block content and installed this basic module. In this article by Matt Butcher, we are going to extend the module's functionality. We will add a few new functions that retrieve and format data from Goodreads.


See More
 




© Packt Publishing Ltd 2010

RSS