How to develop module for drupal 7

http://drupal.org/node/361112

Writing .info files (Drupal 7.x)

Drupal version: Drupal 7.x
Audience: Developers and coders
Last modified: August 20, 2011

Overview

Drupal uses .info files (aka, “dot info files”) to store metadata about themes and modules.

For modules, the .info file is used for:

rendering information on the Drupal Web GUI administration pages;
providing criteria to control module activation and deactivation;
notifying Drupal about the existence of a module;
general administrative purposes in other contexts.

This .info file is required for the system to recognize the presence of a module.
Example

The following is a sample .info file:

name = Example module
description = Gives an example of a module.
core = 7.x
package = Views
dependencies[] = views
dependencies[] = panels
files[] = example.test
configure = admin/config/content/example

The .info file should have the same name as the .module file and reside in the same directory. For example, if your module is named example.module then your .info file should be named example.info.

This file is in standard .ini file format, which defines properties in key/value pairs separated by an equals sign (key = value). You may use quotation marks to wrap the value. Quoted values may contain newlines.

description = Fred’s crazy, crazy module; use with care!

.info files may contain comments. A semi-colon [;] placed at the beginning of a line makes that line a comment, and that line will not be parsed.
Properties

The .info file can contain the following properties:

name (Required)
The displayed name of your module. It should follow the Drupal capitalization standard: only the first letter of the first word is capitalized (“Example module”, not “example module” or “Example Module”). Spaces are allowed as the name is used mainly for the display purposes.

name = Forum

description (Required)
A short, preferably one line description that will tell the administrator what this module does on the module administration page. Remember, overly long descriptions can make this page difficult to work with, so please try to be concise. This field is limited to 255 characters.

description = Enables threaded discussions about general topics.

Descriptions can contain links to documentation and sources. The following example shows a link to the author. It could be a link to a documentation node on Drupal.org. This is useful when the online documentation is better than the readme file and when you want to read about a module before switching it on.
description = Domain manager by Peter Moulding .com.
core (Required)
The version of Drupal that your module is for. For Drupal 7 this would be 7.x, etc. Note that modules cannot specify the minor version of a branch of Drupal. 6.x is correct; 6.2 is not.

core = 7.x

stylesheets (Optional)
Drupal 7 allows you to add CSS files in the module’s .info file if it should be added on every page, just like theme .info files do. Here is an example from the node module’s .info file:

stylesheets[all][] = node.css

scripts (Optional)
You can now add Javascript in the module’s .info file if it should be added on every page. This allows Javascript to be aggregated in an optimal way, and is the preferred method of adding Javascript that most visitors will need on a typical site visit:

scripts[] = somescript.js

For more information see Managing JavaScript in Drupal 7.
files (Optional)
Drupal now supports a dynamic-loading code registry. To support it, all modules must now declare any code files containing class or interface declarations in the .info file, like so:

name = Example module
description = Gives an example of a module.

files[] = example.test

When a module is enabled, Drupal will rescan all declared files and index all the classes and interfaces that it finds. Classes will be loaded automatically by PHP when they are first accessed.
dependencies (Optional)
An array of other modules that your module requires. If these modules are not present, your module cannot be enabled. If these modules are present but not enabled, the administrator will be prompted with a list of additional modules to enable and may choose to enable the required modules as well, or cancel at that point.

The string value of each dependency must be the module filename (excluding “.module”) and should be written in lowercase like the examples below. Spaces are not allowed.

dependencies[] = taxonomy
dependencies[] = comment

If you need to specify that a certain module’s version number is required Drupal 7 provides a way for this in the dependencies[] field. Version numbers are optional and only necessary if the module absolutely requires another module’s specific version or branch.

The syntax for the dependencies[] field(s) is:

dependencies[] = modulename (major.minor)

Where major is the numeric major version number and minor is the numeric or alphanumeric minor version number. x can be used to denote any minor version. Some examples follow.

name = Example
description = An example module
version = 1.0
dependencies[] = exampleapi (1.x)

In the above .info code, the “Example” module requires an “Example API” module with the major version of 1 and any minor version.

dependencies[] = exampleapi (1.0)

This means that the module requires the 1.0 (and only the 1.0) version of the Example API module.

dependencies[] = exampleapi (1.x)

The above module requires any minor version of the module in the 1.x branch (1.0, 1.1, 1.2-beta4, etc.)

The dependencies[] property in the .info file can also optionally specify operators:

= or == equals (optional: equals is the default)
> greater than
= greater than or equal to
1.0)

The above module requires any version greater than version 1.0.

You can optionally specify the core version number as well:

dependencies[] = exampleapi (>7.x-1.5)

The above module requires any minor version of Drupal 7 and any version of the module greater than 1.5.

Additionally, multiple version dependencies can be specified as comma-separated values within the parentheses:

dependencies[] = exampleapi (>1.0, <=3.2, !=3.0)

package (Optional)
If your module comes with other modules or is meant to be used exclusively with other modules, enter the name of the package here. If left blank, the module will be listed as 'Other'. In general, this property should only be used by large multi-module packages, or by modules meant to extend these packages, such as Fields, Views, Commerce, Organic Groups, and the like. All other modules should leave this blank. As a guideline, four or more modules that depend on each other (or all on a single module) make a good candidate for a package. Fewer probably do not. An exception to this rule is the "Development" package, which should be used for any modules which are code development tool modules.

If present, the package string groups modules together on the module administration page (admin/modules); the string should therefore be the heading you would like your modules to appear under, and it needs to be consistent (in spelling and capitalization) in all .info files in which it appears. It should not use punctuation and it should follow the Drupal capitalization standard as noted above.

package = Views

Suggested examples of appropriate items for the package field:

Administration
Commerce
Development
Fields
Media
User interface
Views
Voting (if it uses/requires VotingAPI)

The Package names for contributed modules wiki tries to track current package names; this does not mean they are recommended.
php (Optional)
As of version 6.x, module and themes may specify a minimum PHP version that they require. They may do so by adding a line similar to the following to their .info file:

php = 5.3

That specifies that the module/theme will not work with a version of PHP earlier than 5.3. That is useful if the module makes use of features added in later versions of PHP (improved XML handling, object iterators, JSON, etc.). If no version is specified, it is assumed to be the same as the required PHP version for Drupal core. Modules should generally not specify a required version unless they specifically need a higher later version of PHP than is required by core. See the PHP Manual for further details on PHP version strings.
version (Discouraged)
The version string will be added by drupal.org when a release is created and a tarball packaged. However, if your module is not being hosted on the drupal.org infrastructure, you can give your module whatever version string makes sense.

Users getting their modules directly from git will not have a version string, since the .info files checked into git do not define a version. These users are encouraged to use the git deploy module to provide accurate version strings for the admin/build/modules page for modules in directories checked out directly from git.

Because Drupal core uses a slightly different packaging process than contributed modules, the core modules have a version line predefined. This is an exception to the rule, not the model for contributed modules.
configure (Optional)
As of version 7.x, the path of the module's (main) configuration page.
If a module is enabled, a "Configure" and "Permissions" link appear. This will be the path of the "Configure" link for this particular module on the modules overview page.

configure = admin/config/content/example

required (Optional)

As of version 7.x, modules and themes may specify that they are absolutely required and should never be disabled by adding required = TRUE. These modules will be enabled automatically during install. In most cases it should only be used with the Drupal core required modules (e.g. Node, User, etc.).

hidden (Optional)

As of version 7.x, modules and themes may specify that they should not be visible on the modules page by adding hidden = TRUE. This is commonly used with testing modules used with SimpleTest where end-users should never enable the testing modules.
project (Discouraged, packaging use only)

Module maintainers should not use this at all. The packaging script on drupal.org will automatically place a string here to identify what project the module came from. This is primarily for the Update status module, so that Drupal installations can monitor versions of installed packages and notify administrators when new versions are available.

For more information on info file formatting, see the drupal_parse_info_file() documentation.
Troubleshooting

I added the core = 7.x line, but my module still says "This version is incompatible with the 7.x version of Drupal core" on the modules page. What gives?

Be aware that the "dependencies" format changed between Drupal 5.x and 6.x.

Wrong:
name = Example
description = Example description
dependencies = foo bar ; 5.x dependency format.
core = 6.x

Correct:
name = Example
description = Example description
; Note the [], and that each of the dependencies is on its own line:
dependencies[] = foo
dependencies[] = bar
core = 7.x

Description and Accents

A note to all non-English speaking developers: if you need to use accents, go the ASCII way. It is the only way to prevent truncating this property in the database and still have accents in the description. You cannot use utf8_encode in there obviously.
See also

;; d6 themes .info files ;; http://drupal.org/node/171205
;; d6 modules .info files ;; http://drupal.org/node/231036

.install and the Fields API

Drupal version: Drupal 7.x
Audience: Developers and coders
Page status: Incomplete
Last modified: April 3, 2011

Drupal 6 uses .install files to create database tables and fields, and to insert data. See Writing .install files, (Drupal 6.x) http://drupal.org/node/323314. Drupal 6 .install files can also provide updates to change the database structure and content. Drupal 7 also uses .install files and includes the Field module in core. Field provides a different way to send data to nodes and could replace some .install files. Here is a start for the Drupal 7 .install page.
.install file

In Drupal 7 as in Drupal 6, .install files can define new tables, load data, and implement conversions during updates. This page on conversions from Drupal 6 to Drupal 7, http://drupal.org/node/224333, mentions .install in several places. Follow the Drupal 6 .install documentation and include the following changes. Please help complete this documentation; when you verify that something from the Drupal 6 .install works the same in Drupal 7, note that here.
http://drupal.org/node/224333#registry
http://drupal.org/node/224333#update_php
http://drupal.org/node/224333#schema_translation
http://drupal.org/node/224333#schema_html
http://drupal.org/node/224333#module_file_during_install
http://drupal.org/node/224333#foreign-keys-added
Fields as a replacement

With the Field module, you can add fields to nodes. If the only thing you do in your install file is add fields to nodes by adding a table and some columns, you can now use the Field API, http://api.drupal.org/api/group/field/7, and the field hooks. You can search for the field hooks in http://api.drupal.org/api/7. Search for hook_field_.

hook_field_schema is a hook for adding fields. You can see examples of its use in modules/image/image.field.inc and modules/taxonomy/taxonomy.module. The hook is used in your .module file and might replace the schema in your .install file if you are not adding any other tables. If the only thing in your .install file is a schema, you might not need the .install file.

One other use for the .install file is to delete stuff when someone uninstalls the module. No information yet on how the Field module handles an uninstall.
Dependencies

When you are installing a module that is dependent on another module, the other module might not be 100% functional. Install and test the other module first. During the alpha and beta stage, you can really only depend on Drupal core modules. Add-on modules can be a long way behind. Even core modules can be a problem so, if you have to switch on extra core modules, switch them on one at a time.
White screen of death

Drupal and PHP suppress a lot of errors. If your changes to the .install or .info files produce a blank white screen, the dreaded Drupal White Screen of Death, see Show all errors while developing.

Drupal’s code registry

Drupal version: Drupal 7.x
Page status: Incomplete
Last modified: September 7, 2011

Drupal 7 introduces a code registry – an inventory of all classes and interfaces for all enabled modules and Drupal’s core files. The registry stores the path to the file a given class or interface is defined in, and loads the the file when necessary.

The registry loads classes and interfaces on demand, via php’s built in class autoloading mechanism.

Modules can now move all code for classes that are not regularly used into an include file, and Drupal will only load it on demand. This will cut down on the amount of code loaded per request, and reduce Drupal’s memory footprint.

The only file unconditionally loaded by Drupal for a given module is module_name.module. Any code that must always be available without using the registry should be put in this file.

The registry is rebuilt when modules are enabled or disabled in order to incorporate new code or remove code from disabled modules. If you are developing new code with classes, make sure to rebuild the registry regularly so that new resources are incorporated. (Clearing all caches or calling registry_rebuild() directly both will do this.) Rebuilding an existing registry to incorporate new changes is not that expensive, as the registry will not reparse files that have the same path and modification time as the last build.

Note Files that do not contain classes and interfaces that might be put into use at any time by your module file do not need to be listed with the files[] property in your .info file. Instead, such code can continue to be loaded when you need it, such as through the hook_menu()’s file property. If your module introduces hooks of its own, consider implementing hook_hook_info() to allow other modules’ hook implementations to be autoloaded.

Suppress caching (for development) or to use an external page cache

Drupal version: Drupal 7.x
Last modified: May 12, 2010

when developing for Drupal you might want to suppress all caching of data to make sure new hooks, theme functions, etc are recognized immediately.

This will have performance detriments, of course, so must not be used in production. To turn on this development mode, add the following to your Drupal 7 settings.php file:

if (!class_exists(‘DrupalFakeCache’)) {
$conf[‘cache_backends’][] = ‘includes/cache-install.inc’;
}
// Default to throwing away cache data
$conf[‘cache_default_class’] = ‘DrupalFakeCache’;

// Rely on the DB cache for form caching – otherwise forms fail.
$conf[‘cache_class_cache_form’] = ‘DrupalDatabaseCache’;

in http://drupal.org/node/730046 support for external caches was further enhanced. To use an external page cache (e.g. Varnish) add the following to settings.php:

$conf[‘page_cache_invoke_hooks’] = FALSE;

if (!class_exists(‘DrupalFakeCache’)) {
$conf[‘cache_backends’][] = ‘includes/cache-install.inc’;
}
// Rely on the external cache for page caching.
$conf[‘cache_class_cache_page’] = ‘DrupalFakeCache’;

Then navigate to the Performance page at ?q=admin/config/development/performance and enable the page cache and set a non-zero time for “Expiration of cached pages”:

Using the theme layer (Drupal 7.x)

Drupal version: Drupal 7.x
Audience: Developers and coders, Themers
Last modified: April 5, 2011

A well-made Drupal module allows all elements of its presentation to be overridden by the theme of the site on which it is used. In order for this theme layer to be usable, a module must be written to take advantage of it.

The first step is that logic must be separated as much as possible from presentation. To accomplish this, modules do as much of the work on the data as possible, and hand that data off to the presentation layer. Modules then provide default implementations that provide the basic presentation and serve as a basis for themes that wish to provide an alternate presentation. This is handled through the theme() function. Every chunk of output that is themed through the theme() function is called a theme hook. There are two ways to provide a default implementation. The easier way is to provide a function, and the recommended way is to provide a template and a corresponding preprocessor function. We’ll expand on this in a little bit.

Ideally, your module won’t produce a single line of HTML output that is not in a theme implementation. In reality, administrative pages and very small items may not necessarily be processed through a theme function [theme()], but a sign of a well written module includes easily modified presentation that in all places processes through the theme() function.
Registering theme hooks

In order to utilize a theme hook, your module first has to register that this exists. (This is a change starting in Drupal 6; this registration is necessary due to the amount of time automatic discovery of alternate implementations takes; by registering, this discovery can be done only when needed, and then is always available.)

Your module’s hook_theme() (see the hook API documentation for details on hooks) will return a list of all theme hooks that your module implements. A simple hook_theme implementation might look like this:

array(
‘template’ => ‘forums’,
‘variables’ => array(‘forums’ => NULL, ‘topics’ => NULL, ‘parents’ => NULL, ‘tid’ => NULL, ‘sortby’ => NULL, ‘forum_per_page’ => NULL),
),
//…
);
}
?>

This registration tells us that a theme hook named forums is implemented. The default implementation is a template. Because there are different kinds of template engines, this registration does not include the extension used by the engine, though Drupal core only supports PHPTemplate templates for modules. These template files have the extension ‘.tpl.php’.

It also tells us that the forums theme function takes 6 variables (or arguments), and they all default to NULL. (All arguments must be given defaults as we have no way to assure that a theme(‘forums’, …) call will provide the proper information. If in doubt, make the default NULL). These arguments are translated into the named variables for the template. When calling this theme hook, an author might write:

If the ‘template’ had been left off of the hook_theme() definition, the theme hook’s default implementation would be assumed to be a function named ‘theme_forums’.

There are more options that can be set here, but these two are by far the most important. For more information, see the hook_theme documentation.
Implementing default templates

When implemented as a template, the .tpl.php file is required. It should be in the same directory as the .module file (though the ‘path’ directive can be used to place these templates in another directory or a sub-directory).

Templates should be as much pure HTML as possible, but there are a few functions that are explicitly encouraged in your templates. First is the t() function. Modules should always provide proper translatability, and templates are no exception. Themers need to have the direct text to work with, and translators need all of the text to be passed through t(). Therefore, the use of t() is encouraged in templates.

Another function that is encouraged in templates is format_date(). Since this function is, really, a presentation function, the presentation layer is the appropriate place for it. However, its use is somewhat arcane and difficult for people who are not familiar with PHP to use. Nonetheless, it should be used in templates.

For other functions, consider whether or not they really are needed at the presentation layer. If they are not, they can be used in the preprocessor layer. All templates may have an optional preprocess function, named template_preprocess_HOOK. For example, for our forums theme hook above, its preprocess function will be named template_preprocess_forums().

The purpose of the preprocess function is to perform any logic that needs to be performed to make data presentable, and to sanitize any data so that it is safe to output. It is critically important that your output be secure and not contain XSS (Cross Site Scripting) vulnerabilities. And since data that is output often comes from users, this data must be sanitized before it is output. Since we assume that themers are not necessarily developers, we must assume that they are not going to fully understand how to do this; but that’s ok, because we can sanitize data in the preprocess function by running it through check_plain, check_markup, filter_xss_admin or other output sanitizing functions.

Here is a simple example from Poll module:

First, note that the preprocessor function takes a reference to an array of variables. This array will be seeded with the arguments that were sent via the theme() and named by the ‘arguments’ section of the hook registration. Since this is a reference, simply modifying this array is enough to transport those changes to the template that accompanies it.

This example illustrates three important concepts:

The ‘title’ field is unsafe, because it comes from user input. It is run through check_plain so that the template may safely output it.
The theme hook receives the total number of votes and the number of votes for just that item, but the template wants to display a percentage. That kind of work shouldn’t be done in a template; instead, the math is performed here. The variables still exist, though; a themer overriding this could easily choose to display something other than a percentage.
The special variable ‘theme_hook_suggestions’ can be used to provide alternative template files to use. This is an array of hook names, and it is last in, first out, which means the last item added to the array will be the first one used. If a template doesn’t exist, it will use the next one in the list. The special double underscore in this example is a shorthand way of indicating this– it does not look for a hook name with two underscores or a template with two dashes, rather, it will look for (in this template file case) first poll-bar-block.tpl.php, and failing to find that it will drop the part after the double underscores and it will look for poll-bar.tpl.php.

Theme Developer module, which is part of the devel project, includes a template log feature which outputs at bottom of page all the template files which could have been used to theme the current page. This may be handy while building your module, but even more so when themeing a site. Also, don’t miss its Themer information popup.

Quick note: Template files should be named with hyphens instead of underscores. If the theme hook is ‘forum_list’, the template file should be named ‘forum-list.tpl.php’.
Implementing theme functions

Drupal allows you to use functions for your default theme implementations. This is somewhat faster performance than loading template files. New in Drupal 7, theme functions can have preprocess functions just like templates. Copying and modifying template files is still considered more friendly for themers than overriding theme functions in template.php.

Theme functions are named by prepending ‘theme_’ to the name of the hook. The arguments given to theme(‘hook’) will be passed straight through, unaltered. The defaults specified in the hook registration will not be provided here; they must be provided as normal PHP argument defaults.

array(
‘render element’ => ‘element’,
),
// …
);
}
?>

And the function:

<?php
/**
* Returns HTML for the entire dashboard.
*
* @param $variables
* An associative array containing:
* – element: A render element containing the properties of the dashboard
* region element, #dashboard_region and #children.
*
* @ingroup themeable
*/
function theme_dashboard($variables) {
extract($variables);
drupal_add_css(drupal_get_path('module', 'dashboard') . '/dashboard.css');
return '

‘ . $element[‘#children’] . ‘

‘;
}
?>

Themers can override this function by creating a function named themename_dashboard().
Dynamic theming

In addition to being able to specify alternate templates in a preprocess function, you may also create dynamic theming implementations using wildcards. There are two steps in this process.

First, in hook_theme, you can specify a pattern. Patterns are simple regular expressions. ^ (beginning of line) is assumed, but $ (end of line) is not. To signify the dynamic portion of the pattern, a double underscore is the general convention; this is not required but it is highly recommended.

Second, when calling the theme() function, instead of a string for the first argument you may pass an array. This array is much like theme_hook_suggestions above, but this one is first in, first out so the first one seen will be used.

For a practical example, the module Views likes to let each view be themed by name. Upon registration, the hook ‘views_view’ would register with the pattern ‘views_view__’. When theming that view, Views would call:
$output = theme(array(“views_view__$view->name”, ‘views_view’), $view);

Views will implement a default view for views_view; if a theme registers ‘views_view__foo’ and Views themes a view named ‘foo’, the specific override will activate and be used instead. Unlike the ‘theme_hook_suggestions’ variable in the preprocessor function, this works for both theme functions as well as templates.

More on preprocess functions.
theme(‘table’) and theme(‘item_list’)

Drupal provides a few helpers to build complex HTML constructs easily. These are very useful features, and by using them it is easy to create a consistent look on tables and lists. The downside is that they are not readily accessible to a themer. Instead, they place code that should be at the presentation layer into the logic layer, and only advanced themers are able to do anything with it.

These functions are more acceptable for administration pages.

When creating output that is likely to be changed, it is best to avoid the use of these constructs and create the tables and lists with real HTML code. The forum themes are perfect examples of how to accomplish this and still create HTML code that is consistent.
Function name suggestions with double underscores

An even bigger problem with using common provided theme functions such as theme(‘item_list’) is that a theme probably does not want to override every theme_item_list() for every list output by Drupal anywhere, but only the one in our module. Therefore, instead of simply theme(‘item_list’), we can use theme(‘item_list__mymodule__main’, $items) which would make it possible for a theme to implement themename_item_list__mymodule__main() or themename_item_list__mymodule() to override item_list() only for that specific instance.

An alternative to the double underscore convention is to explicitly name each hook that could be used, in the order of our preference, in an array:

Having changes to code take effect

When new theming functions are added, we must cleare the theme registry to see them.
See also

Theme handbook
Drupal API documentation for hook_theme()
Drupal API documentation for theme()

‹ Suppress caching (for development) or to use an external page cache up Integrating third party applications ›
Login or register to post comments
Comments
Incorrect theme() function usage
Posted by math2k on August 4, 2011 at 2:27pm

Hi,

The theme() usage described above is incorrect for Drupal7. $variables must now be passed as an array rather than a list of parameters.

In this example, I’d use the following (not tested):
$forums, ‘topics’ => $topics, ‘parents’ => $parents, ‘tid’ => 17, ‘sortby’ => ‘ASC’, ‘forum_per_page’ => 25);
$output = theme(‘forums’, $params);
?>

Hope it helps,

Math

Login or register to post comments

Missunderstanding?
Posted by Nelson1337 on October 7, 2011 at 11:39am

In the text above it’s mentioned, that the last value of the theme_hook_suggestions-Array is used at first, then if it doesn’t exist , the next to last value and so on. (In my point of view, this means from right to left.)

In my Drupal 7 reference book it is written that theme() takes the array and checks them from left to right.
What’s correct?

Edit:
This example is from my reference book:

I’d say, that theme() will firstly look for theme hook called ‘menu_tree__menu_block’, then ‘menu_tree__menu_block’ . $menu_name and eventually if necessary for ‘menu_tree’.

Is there a difference when I put these theme hook suggestion into the $theme_hook_suggestions[] like it is shown above in the context?

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s