Drupal is a free and open source content management system written in PHP. The standard release of Drupal, Drupal core, contains basic features typical to content management systems, such as menu management, page layout customization and content publishing. However, its main strength are its community-contributed addons, known as modules, which make it possible to alter and extend Drupal’s capabilities to one’s wishes.
This article is about implementing such a module. There are other good tutorials on this. This one is going to cover the specific case of creating a text file on the server with the content of certain nodes every time a node is created, updated or deleted. The tutorial delves into some of the most important Drupal APIs, such as the Database, File and Form APIs.
People reading this should have added a module to a Drupal installation before and be somewhat familiar with PHP.

1. Drupal Modules

A module is a collection of php functions that link into Drupal, providing additional functionality to the Drupal installation.
Because the module code executes within the Drupal context, it can access all the variables and structures and use all the functions of Drupal core.

1.1. Choose a short name

The first step in creating a module is to choose a short name for it. This short name will be used in all file and function names in your module. In this example, we will be using “nodes_to_text_file” as the short name.

Make sure your short name starts with a letter and only contains lower-case letters and underscores. Also make sure your module does not have the same short name as any theme you will be using.

1.2. Create the directory

In the modules directory of your Drupal 7 install, create a folder with your short name as its name.

1.3 empty module file

Create a nodes_to_text_file.module file in your nodes_to_text_file directory. It is to this file that we will add the functions that will hook into Drupal. In the next section, we will add the first function.
Module files begin with the opening php tag. Add it to the module file:

<?php

However, omit the closing php tag ?>. Omitting it is a convention, and including it might cause strange runtime behavior on certain server setups.

1.4 Creating the info file

Every module has its info file. It contains metadata about the module and hence, tells Drupal about the module. Without this file, the module will not appear in the module listing of your Drupal installation.

name = Nodes To Text File
description = A module that creates a text file containing all content of certain types of nodes whenever a node of one of these types is added, edited or deleted
core = 7.x

The above 3 properties form the minimal setup: name sets the name of the module, description describes what the module does and core determines with which version of Drupal this module is meant to be used.

After adding the info file, the module should be visible in your module listing. Go to Modules and scroll down to the “Other” section. The Nodes To Text File module should be listed there. Enable it.

2. Module Hooks

Hooks are the most important concept to grasp when implementing Drupal modules. Hooks provide the points at which you can insert your actions. They can be thought of as event listeners. An event such as deleting a node would trigger the hook “hook_node_delete”. In this case, Drupal will scan all modules for functions with the name mymodule_node_delete, where mymodule is the module’s name. If your module implements hook_node_delete, that function will always execute after a node gets deleted.

2.1 Implementing the hook_help hook

The first hook we are going to implement is hook_help. This hook should always be implemented. It provides help and information about the module. To implement an hook, you need to replace “hook” in the hook name(f.e. hook_help) with your module’s short name and create a function in the .module file with that name.
In this case, nodes_to_text_file_help:

/**
* Implements hook_help.
*
* Displays help and module information.
*
* @param path
* Which path of the site we're using to display help
* @param arg
* Array that holds the current path as returned from arg() function
*/
function nodes_to_text_file_help($path, $arg) {
switch ($path) {
case "admin/help#nodes_to_text_file":
return '<p>'. t("A module that creates a text file containing all content of certain types of nodes whenever a node of one of these types is added, edited or deleted") .'</p>';
break;
}
}

The path parameter contains the information about where the user is currently at. If the user is currently at the help section of our module, we return a paragraph that gives information about our module. Note that we use the Drupal t($string) function, which translates the content if possible and which should be used on any string that is shown to the user.

Now, check Modules page of your Drupal site again. You should see a link ‘Help’ on the right of your module. If you click it, you will see the paragraph returned by the nodes_to_text_file_help function of our module.

This query uses the new(starting from Drupal 7) db_select function of the Database API. Other than the old db_query function, db_select makes it possible to construct the query in an object oriented matter.

db_select maps to a sql select statement(as do db_insert, db_update and db_delete map to their sql equivalents). It takes a table name(f.e. ‘node’) and an alias (f.e. ‘n’) as its arguments. This comes down to:

select * from node as n

fields selects the fields listed in the array in its second argument on the alias n:

select nid, title, created from node as n

condition adds a condition to the query. The first is the field, the second the value, the third the operator:

select nid, title, created from node as n where published = 1 and type in ('article','page') and uid = 1

if the values in the $contentTypes array are ‘article’ and ‘page’ and user Steffen has uid 1. Note that the “global $user” statement gets the global $user variable, which contains all information about the user that is currently logged in.

orderBy then sort the results according to the field in its first argument:

select nid, title, created from node as n where published = 1 and type in ('article','page') and uid = 1 order by created asc

The execute method then compiles and runs the query and returns the result.

The other query we need – one that fetches all content types of the core Node Module – uses the same API and is quite simular:

4. Drupal File API

We are now implementing our nodes_to_text_file_write_file function that is called by the node_insert, node_update and node_delete hooks. We briefly discuss the part in which we determine whether the file should be written and in which we fetch the contents first.

First, we fetch the node content types which should be included in the file using variable_get($name,$default) As we will see in the next section, variable_set($name,$value) – typically invoked in a module configuration form – sets Drupal system wide variables, which can then be fetched by variable_get.
We then match the node type of the node that was inserted, updated or deleted with the array of content types. If a match is found($elementFound), execution continues. We fetch the nodes using the query of the previous section and get a reference to the username of the currently logged in user.

Then – where the … dots are in the above function – we add the following and write out the fetched content to a file:

We are using some functions of the Drupal File API here:file_build_uri: This function constructs, given a relative path, a URI into Drupal’s default files location(this is usually something like ‘sites/default/files/’).file_prepare_directory: This function checks that the directory exists and is writable.drupal_mkdir: This function creates a directory using Drupal’s default mode.

To respectively open the file for writing, write to it and close it again, we use the php functions fopen, fwrite and fclose.

5. Drupal Form API

Our module is implemented now. However, we want to be able to configure the node content types for which the file should be written. We already saw that we were fetching this array of content types like this:

Using the hook_menu hook, modules register paths to define how URL requests are handled. In this case, we are setting a path that makes a configuration form available from the Drupal Configuration page:

We passed the form already to the array of the ‘page arguments’ property of the link, but we didnt define it yet. We are doing so now:

after which we return the form. Note that the Form API is pretty straightforward. It is generally enough to consult the API page to know what types of fields are available and which properties are available and which ones are required.