site map

Parkside Web Development

Read... Write... Share...

May 17, 2008

A First Glance at Writing Drupal Modules

Filed under: Developing the WebThe Quagmire @ 9:02 am

I’ve just begun a project that involves extending Drupal in several ways. It appears that I’ll have to create at least two modules and possibly a filter or two.

I began by laying out the data structure that I’ll need to support one of my new modules. I started with the simplest of the two, to ease into things and get my feet wet. Once my database was setup, I spent a couple of hours reading through Drupal online docs and looking at some existing custom node modules. After a few false starts I found some bare bones code to begin work from.

Turns out that the module directory only really requires two files to start from; modulename.info & modulename.module. The .info file is pretty straight forward. It consists of 10 lines of text that describe the module. Here’s an example… the file should be named modulename.info and you’ll replace modulename with your module directory name all through both files:

1
2
3
4
5
6
7
8
9
10
; $Id: modulename.info,v 1.1.2.3.2.1 2007/09/05 14:05:48 snpower Exp $
name = The Name of Your Module
description = "This should be a description of your custom node module."
core = 6.x
 
; Information added by drupal.org packaging script on 2008-04-30
version = "6.x-1.6"
core = "6.x"
project = "modulename"
datestamp = "1209552907"

Next we move on to the modulename.module. I put together this bare bones module script from information in the Drupal Documentation. It’s enough to build a simple custom node module that doesn’t do much of anything. It is however a pretty good starting place. To use this module you’ll need to create the following database table in your Drupal database.

1
2
3
4
5
6
7
8
CREATE TABLE node_example (
     vid int(10) unsigned NOT NULL default '0',
     nid int(10) unsigned NOT NULL default '0',
     color varchar(255) NOT NULL default '',
     quantity int(10) unsigned NOT NULL default '0',
     PRIMARY KEY (vid, nid),
     KEY `node_example_nid` (nid)
)

Once the database is setup, you’ll want to create your modulename.module file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
<?php
function node_example_access($op, $node, $account) {
  if ($op == 'create') {
    return user_access('create example content', $account);
  }
 
  if ($op == 'update') {
    if (user_access('edit any example content', $account) || (user_access('edit own example content', $account) && ($account->uid == $node->uid))) {
      return TRUE;
    }
  }
 
  if ($op == 'delete') {
    if (user_access('delete any example content', $account) || (user_access('delete own example content', $account) && ($account->uid == $node->uid))) {
      return TRUE;
    }
  }
}
 
 
function node_example_delete($node) {
  // Notice that we're matching all revision, by using the node's nid.
  db_query('DELETE FROM {node_example} WHERE nid = %d', $node->nid);
}
 
 
function node_example_form(&$node) {
  // The site admin can decide if this node type has a title and body, and how
  // the fields should be labeled. We need to load these settings so we can
  // build the node form correctly.
  $type = node_get_types('type', $node);
 
  if ($type->has_title) {
    $form['title'] = array(
      '#type' => 'textfield',
      '#title' => check_plain($type->title_label),
      '#required' => TRUE,
      '#default_value' => $node->title,
      '#weight' => -5
    );
  }
 
  if ($type->has_body) {
    // In Drupal 6, we can use node_body_field() to get the body and filter
    // elements. This replaces the old textarea + filter_form() method of
    // setting this up. It will also ensure the teaser splitter gets set up
    // properly.
    $form['body_field'] = node_body_field($node, $type->body_label, $type->min_word_count);
  }
 
  // Now we define the form elements specific to our node type.
  $form['color'] = array(
    '#type' => 'textfield',
    '#title' => t('Color'),
    '#default_value' => isset($node->color) ? $node->color : '',
  );
  $form['quantity'] = array(
    '#type' => 'textfield',
    '#title' => t('Quantity'),
    '#default_value' => isset($node->quantity) ? $node->quantity : 0,
    '#size' => 10,
    '#maxlength' => 10
  );
 
  return $form;
}
 
function node_example_insert($node) {
  db_query("INSERT INTO {node_example} (vid, nid, color, quantity) VALUES (%d, %d, '%s', %d)", $node->vid, $node->nid, $node->color, $node->quantity);
}
 
function node_example_load($node) {
  $additions = db_fetch_object(db_query('SELECT color, quantity FROM {node_example} WHERE vid = %d', $node->vid));
  return $additions;
}
 
function node_example_nodeapi(&$node, $op, $teaser, $page) {
  switch ($op) {
    case 'delete revision':
      // Notice that we're matching a single revision based on the node's vid.
      db_query('DELETE FROM {node_example} WHERE vid = %d', $node->vid);
      break;
  }
}
 
function node_example_node_info() {
  return array(
    'node_example' => array(
      'name' => t('Example node'),
      'module' => 'node_example',
      'description' => t("This is an example node type with a few fields."),
      'has_title' => TRUE,
      'title_label' => t('Example Title'),
      'has_body' => TRUE,
      'body_label' => t('Example Body'),
    )
  );
}
 
function node_example_perm() {
  return array(
    'create example content',
    'delete own example content',
    'delete any example content',
    'edit own example content',
    'edit any example content',
  );
}
 
function node_example_theme() {
  return array(
    'node_example_order_info' => array(
      'arguments' => array('node'),
    ),
  );
}
 
function node_example_update($node) {
  // if this is a new node or we're adding a new revision,
  if ($node->revision) {
    node_example_insert($node);
  }
  else {
    db_query("UPDATE {node_example} SET color = '%s', quantity = %d WHERE vid = %d", $node->color, $node->quantity, $node->vid);
  }
}
 
function node_example_validate(&$node) {
                                                                                                                                                             if ($node->quantity) {
    if (!is_numeric($node->quantity)) {
      form_set_error('quantity', t('The quantity must be a number.'));
    }
  }
  else {
    // Let an empty field mean "zero."
    $node->quantity = 0;
  }
}
 
function node_example_view($node, $teaser = FALSE, $page = FALSE) {
  $node = node_prepare($node, $teaser);
  $node->content['myfield'] = array(
    '#value' => theme('node_example_order_info', $node),
    '#weight' => 1,
  );
 
  return $node;
}
 
function theme_node_example_order_info($node) {
  $output = '<div class="node_example_order_info">';
  $output .= t('The order is for %quantity %color items.', array('%quantity' => check_plain($node->quantity), '%color' => check_plain($node->color)));
  $output .= '</div>';
  return $output;
}
?>

From here it becomes a case of figuring out what each of the functions does. Fairly straight forward work and once you get the hang of what’s going on and how Drupal works under the hood.

I find Drupal to be a fairly flexible tool for website managers. It does all the basic stuff that you’d want a site to do (pages, blocks, forms, users, comments). The flexibility ends when you want to build a complex custom form for editing your node type. For example the forms api gives you pretty good ability to build BASIC forms. Once you stray from simple forms, things can get a little difficult. For example, I needed a grid of field inputs with labels across the top. As far as I can tell, Drupal doesn’t support this. I ended up using the “markup” type to place custom html directly into the form. Here’s a chunk of code that illustrates what the markup type does:

1
2
3
4
5
6
7
8
9
10
11
12
13
function registration_form(&$node) {
  $tablehtml = "<table>";
  $tablehtml .= "<tr><th>Header 1</th><th>Header2</th></tr>";
  $tablehtml .= "<tr><td><input type='text' value='value1' /></td>";
  $tablehtml .= "<td><input type='text' value='value12 /></td></tr>";
  $tablehtml .= "</table>";
  $form['teamdata'] = array(
    '#type' => 'markup',
    '#value' => $tablehtml,
    '#weight'=>-5
  );
  return $form;
}

So far I’d have to say that I’m a fan of Durpal. But there are some things that I think could be done a little better. For example I think it would be good to build on an object oriented platform and Drupal is rife with global variables and functions. I think the admin tools have a bit of a learning curve. Parhaps too much for beginning webmasters. Although, I think the interface is simple compared to Joomla. The upside is the large community of developers building modules and themes. Many sites could get by with a basic install of Drupal + a couple modules and a theme/face lift.

Share/Save/Bookmark


3 Comments »

[...] Go to the author’s original blog: A First Glance at Writing Drupal Modules [...]

Elvis — May 19, 2008 @ 6:09 pm

Welcome to the Drupal project and community!

Inside the includes/theme.inc file is a lot of theme functions that you have at your disposal. I would take a look there to to learn about “output” options.

Inside your theme directory, you can have a template.php file where you can override theme functions from the theme layer. As well as pick up and override phptemplate objects too.

If you have seen / or read the Pro Drupal Developer, you can check it out here:
http://www.drupalbook.com

You can get the free chapter (theming Drupal) here:
http://www.apress.com/book/downloadfile/3486

We hope you stick around with Drupal.

The Quagmire — May 19, 2008 @ 7:14 pm

Hey Elvis,

Thanks for the comment and the suggestions. Although I’m fairly new to drupal I was able to jump into it pretty quickly. As I said above, the system seems fairly flexible. What I needed to do was generate a grid of input boxes and didn’t see a reasonable way to do it with the default api calls. That’s why I resorted to the markup type. If you know of a simpler way of accomplishing this, I’d love to hear about it.

RSS feed for comments on this post. TrackBack URL

Leave a comment