How to create your own simple node type (4.7)
How to create your own simple node type...
- A simple node type is the most basic of node types such as 'page' and 'story' node types, with a title, teaser, body, etc.
- Why would you want or need this...
The answer stems from a discussion about the Difference Between Page and Story
and that ultimately each node type allows for separate theming, categorization, descriptions, etc... - This example walks you through copying the 'story.module' and making the appropriate changes to the php code for your new simple node type.
- There are no database changes or hacking of code, the result will be a new module file that you will need to upload into its own module directory.
- What you need...
- means to download/upload files to your modules directory
- a text editor
- a little php knowledge is helpful, but not required
- some information about your node type
- a technical-name for your node type - limit 32 characters - don't use the name of an existing module - no spaces, numbers or punctuation unless you know what you're doing
- a user-friendly-name for your node type (and a plural version of this name) (this is how your node type will appear in most places on your site) - spaces and numbers are ok but again no punctuation unless you know what you're doing, note: you can use the same name as your technical-name above
- a module-description - a short description of your node type that will appear on the administer->modules page - don't use quotes or apostrophes unless you know what you are doing
- a create-content description - a short description of your node type that will appear on the create content page - don't use quotes or apostrophes unless you know what you are doing
- a help-description - a short description of your node type that will appear on the help page - don't use quotes or apostrophes unless you know what you are doing
- drupal-help-text and link - (these are specifically provided for you below)
- an optional title-caption for the title field in the node entry/edit form if something other than 'Title' is desired - don't use quotes or apostrophes unless you know what you are doing
- an optional body-caption for the body field in the node entry/edit form if something other than 'Body' is desired - don't use quotes or apostrophes unless you know what you are doing
- In the following bolded text in the code boxes is intended to show where the code changes. Quotes and apostrophes in the code are important, please place close attention.
As an example... say you want a simple press release node type.
- technical-name = release
- user-friendly-name = press release
- user-friendly-plural = press releases
- module-description = Enables the creation of press releases.
- create-content-description = Create a press release.
- help-description = Press Releases are notices sent to various media outlets informing them of some note-worthy information.
- drupal-help-text = This module was created by [your name here], following directions posted at drupal.org <a href="%release">How to create your own simple node type (from story node) (Drupal 4.7)</a>
IMPORTANT: Note the use of our technical-name release in the drupal help text above (underlined for emphasis only), you should change the underlined text to your technical-name. You should also change [your name here] to your actual name. The rest of the text should be left as is (unless you know what you are doing). - drupal-help-link = http://www.drupal.org/node/40684
- title-caption (optional) = Press Release Title
- body-caption (optional) = Press Release Body
I specifically use an example that has a different technical-name and user-friendly-name so that it is a little more clear in the changes where the two are used, but many node-types that exist (or that you may want to create) use the same value.
- Download the story.module file from the modules directory
- Save it as your technical-name .module in our example the file will be named release.module
- Open the new file in your text editor
Now, without getting into too many details on drupal modules and php code....
The first line of code should be <?phpdon't touch that
The next line of code will be something like...
// $Id: story.module,v 1.179 2005/12/05 09:11:33 dries Exp $You should remove this, so that there is no confusion with the story module
The next couple lines of code should be
/**
* @file
* Enables users to submit <strong>stories, articles or similar content</strong>.
*/change the bolded text above to your user-friendly-plural, in our example press releases seen below
/**
* @file
* Enables users to submit <strong>press releases</strong>.
*/The story.module file allows for story node types. The module file implements the following 7 drupal hooks
- hook_help
- hook_node_info
- hook_perm
- hook_access
- hook_menu
- hook_form
Before each hook is implemented you should you will see a php comment
for hook_help...
/**
* Implementation of hook_help().
*/and for hook_node_info...
/**
* Implementation of hook_node_info().
*/etc...
After the comment, the hook function is called, but instead of the word hook the word story is used as this module was originally for story nodes.
for hook_help...
function story_help($section) {and for hook_node_info...
function story_node_info() {etc...
In the function calls change story to your technical-name, in our example release
for hook_help...
function <strong>story</strong>_help($section) {becomes
function <strong>release</strong>_help($section) {and for hook_node_info
function <strong>story</strong>_node_info() {becomes
function <strong>release</strong>_node_info() {etc...
We will be changing all 7 in the following steps, plus a few more things...
Here are the changes, moving down the file, starting at the top
Again noting that in the following bolded text in the code boxes is intended to show where the code changes. Quotes and apostrophes in the code are important, please place close attention.
1. hook_help
hook_help shows helpful information in various places...
NOTE - this function has the most changes, so if you can get thru this one, the rest are a breeze.
for the curious: this 4.7 hook_help implementation was expanded with additional help text from 4.6
hook_help original
function <strong>story</strong>_help($section) {
switch ($section) {
case 'admin/help#<strong>story</strong>':
$output = '<p>'. t('The <strong>story</strong> module is used to create a
content post type called <em><strong>stories</strong>.</em>
<strong>Stories are articles in their simplest form: they have a title,
a teaser and a body. Stories are typically used to post news articles
or as a group blog.</strong>') .'</p>';
$output .= '<p>'. t('The <strong>story</strong> administration interface allows
for complex configuration. It provides a submission form, workflow,
default view permission, default edit permission, permissions for permission,
and attachments. Trackbacks can also be enabled.') .'</p>';
$output .= t('<p>You can</p>
<ul>
<li>post a <strong>story</strong> at <a href="%node-add-<strong>story</strong>">
create content >> <strong>story</strong></a>.</li>
<li>configure <strong>story</strong> at <a href="%admin-node-configure-types">
administer >> content >> configure types >>
<strong>story</strong> configure</a>.</li>
</ul>
', array('%node-add-<strong>story</strong>' => url('node/add/<strong>story</strong>'),
'%admin-node-configure-types' => url('admin/node/configure/types')));
$output .= '<p>'. t('<strong>For more information please read the configuration
and customization handbook <a href="%story">Story page</a>.</strong>',
array('%<strong>story</strong>' => '<strong>http://www.drupal.org/handbook/modules/story/</strong>')) .'</p>';
return $output;
case 'admin/modules#description':
return t('<strong>Allows users to submit stories, articles or similar content</strong>.');
case 'node/add#<strong>story</strong>':
return t('<strong>Stories are articles in their simplest form: they have a title,
a teaser and a body, but can be extended by other modules.
The teaser is part of the body too.
Stories may be used as a personal blog or for news articles.</strong>');
}
}to change this function, we need most of our new node type information...
function <strong>technical-name</strong>_help($section) {
switch ($section) {
case 'admin/help#<strong>technical-name</strong>':
$output = '<p>'. t('The <strong>technical-name</strong> module is used to create a
content post type called <em><strong>user-friendly-plural</strong>.</em>
<strong>help-description</strong>') .'</p>';
$output .= '<p>'. t('The <strong>user-friendly-name</strong> administration interface allows
for complex configuration. It provides a submission form, workflow,
default view permission, default edit permission, permissions for permission,
and attachments. Trackbacks can also be enabled.') .'</p>';
$output .= t('<p>You can</p>
<ul>
<li>post a <strong>user-friendly-name</strong> at <a href="%node-add-<strong>technical-name</strong>">
create content >> <strong>user-friendly-name</strong></a>.</li>
<li>configure <strong>user-friendly-name</strong> at <a href="%admin-node-configure-types">
administer >> content >> configure types >>
<strong>user-friendly-name</strong> configure</a>.</li>
</ul>
', array('%node-add-<strong>technical-name</strong>' => url('node/add/<strong>technical-name</strong>'),
'%admin-node-configure-types' => url('admin/node/configure/types')));
$output .= '<p>'. t('<strong>drupal-help-text</strong>',
array('%<strong>technical-name</strong>' => '<strong>drupal-help-link</strong>')) .'</p>';
return $output;
case 'admin/modules#description':
return t('<strong>module-description</strong>.');
case 'node/add#<strong>technical-name</strong>':
return t('<strong>create-content-description</strong>');
}
}hook_help using our press release example
function <strong>release</strong>_help($section) {
switch ($section) {
case 'admin/help#<strong>release</strong>':
$output = '<p>'. t('The <strong>release</strong> module is used to create a
content post type called <em><strong>press releases</strong>.</em>
<strong>Press Releases are notices sent to various media outlets
informing them of some note-worthy information.</strong>') .'</p>';
$output .= '<p>'. t('The <strong>press release</strong> administration interface allows
for complex configuration. It provides a submission form, workflow,
default view permission, default edit permission, permissions for permission,
and attachments. Trackbacks can also be enabled.') .'</p>';
$output .= t('<p>You can</p>
<ul>
<li>post a <strong>press release</strong> at <a href="%node-add-<strong>release</strong>">
create content >> <strong>press release</strong></a>.</li>
<li>configure <strong>press release</strong> at <a href="%admin-node-configure-types">
administer >> content >> configure types >>
<strong>press release</strong> configure</a>.</li>
</ul>
', array('%node-add-<strong>release</strong>' => url('node/add/<strong>release</strong>'),
'%admin-node-configure-types' => url('admin/node/configure/types')));
$output .= '<p>'. t('<strong>This module was created by [your name here],
following directions posted at drupal.org <a href="%<u>release</u>">
How to create your own simple node type (from story node) (Drupal 4.7)</a></strong>',
array('%<strong>release</strong>' => '<strong>http://www.drupal.org/node/40684</strong>')) .'</p>';
return $output;
case 'admin/modules#description':
return t('<strong>Enables the creation of press releases.</strong>.');
case 'node/add#<strong>release</strong>':
return t('<strong>Create a press release.</strong>');
}
}2. hook_node_info
hook_node_info registers the node type with drupal
for the curious: hook_node_info is new in 4.7 and replaced hook_node_name in 4.6
hook_node_info original
function <strong>story</strong>_node_info() {
return array('<strong>story</strong>' =>
array('name' => t('<strong>story</strong>'),
'base' => '<strong>story</strong>'));
}to change this function, we need our technical-name and our user-friendly-name
function <strong>technical-name</strong>_node_info() {
return array('<strong>technical-name</strong>' =>
array('name' => t('<strong>user-friendly-name</strong>'),
'base' => '<strong>technical-name</strong>'));
}hook_node_info using our press release example
function <strong>release</strong>_node_info() {
return array('<strong>release</strong>' =>
array('name' => t('<strong>press release</strong>'),
'base' => '<strong>release</strong>'));
}3. hook_perm
hook_perm assigns names for permissions that may exist for this node type (these need to agree with hook_access below)
for the curious: this 4.7 hook_perm implementation is the same as the 4.6 implementation
hook_perm original
function <strong>story</strong>_perm() {
return array('create <strong>stories</strong>',
'edit own <strong>stories</strong>');
}to change this function, we need our technical-name and our user-friendly-plural
function <strong>technical-name</strong>_perm() {
return array('create <strong>user-friendly-plural</strong>',
'edit own <strong>user-friendly-plural</strong>');
}hook_perm using our press release example
function <strong>release</strong>_perm() {
return array('create <strong>press releases</strong>',
'edit own <strong>press releases</strong>');
}4. hook_access
hook_access checks your permissions against a user and what the permission allows (these need to agree with hook_perm above)
for the curious: this 4.7 hook_access implementation is the same as the 4.6 implementation
hook_access original
function <strong>story</strong>_access($op, $node) {
global $user;
if ($op == 'create') {
return user_access('create <strong>stories</strong>');
}
if ($op == 'update' || $op == 'delete') {
if (user_access('edit own <strong>stories</strong>')
&& ($user->uid == $node->uid)) {
return TRUE;
}
}
}to change this function, we need our technical-name and user-friendly-plural
function <strong>technical-name</strong>_access($op, $node) {
global $user;
if ($op == 'create') {
return user_access('create <strong>user-friendly-plural</strong>');
}
if ($op == 'update' || $op == 'delete') {
if (user_access('edit own <strong>user-friendly-plural</strong>')
&& ($user->uid == $node->uid)) {
return TRUE;
}
}
}hook_access using our press release example
function <strong>release</strong>_access($op, $node) {
global $user;
if ($op == 'create') {
return user_access('create <strong>press releases</strong>');
}
if ($op == 'update' || $op == 'delete') {
if (user_access('edit own <strong>press releases</strong>')
&& ($user->uid == $node->uid)) {
return TRUE;
}
}
}5. hook_menu
hook_menu allows the node type to show in various menus, in this case just the create content menu (note the 'create' permission should agree with above)
for the curious: this 4.7 hook_menu implementation is the same as the 4.6 implementation
hook_menu original
function <strong>story</strong>_menu($may_cache) {
$items = array();
if ($may_cache) {
$items[] = array('path' => 'node/add/<strong>story</strong>',
'title' => t('<strong>story</strong>'),
'access' => user_access('create <strong>stories</strong>'));
}
return $items;
}to change this function, we need our technical-name, user-friendly-name and user-friendly-plural
function <strong>technical-name</strong>_menu($may_cache) {
$items = array();
if ($may_cache) {
$items[] = array('path' => 'node/add/<strong>technical-name</strong>',
'title' => t('<strong>user-friendly-name</strong>'),
'access' => user_access('create <strong>user-friendly-plural</strong>'));
}
return $items;
}hook_menu using our press release example
function <strong>release</strong>_menu($may_cache) {
$items = array();
if ($may_cache) {
$items[] = array('path' => 'node/add/<strong>release</strong>',
'title' => t('<strong>press release</strong>'),
'access' => user_access('create <strong>press releases</strong>'));
}
return $items;
}6. hook_form hook_form allows for a form for adding, editing and deleting nodes of the type for the curious: this 4.7 implementation of hook_form is substantially different from 4.6 hook_form original
function <strong>story</strong>_form(&$node) {
$form['title'] = array('#type' => 'textfield',
'#title' => t('<strong><u>Title</u></strong>'),
'#required' => TRUE, '#default_value' => $node->title);
$form['body'] = array('#type' => 'textarea',
'#title' => t('<strong><u>Body</u></strong>'),
'#default_value' => $node->body, '#rows' => 20, '#required' => TRUE);
$form['format'] = filter_form($node->format);
$form['log'] = array('#type' => 'fieldset', '#title' => t('Log message'),
'#collapsible' => TRUE, '#collapsed' => TRUE);
$form['log']['message'] = array('#type' => 'textarea', '#default_value' => $node->log,
'#description' => t('An explanation of the additions or updates being made to
help other authors understand your motivations.')
);
return $form;
}to change this function, we need our technical-name and our two optional captions (note the optional captions are underlined for emphasis and do not need to be changed)
function <strong>technical-name</strong>_form(&$node) {
$form['title'] = array('#type' => 'textfield',
'#title' => t('<strong><u>title caption</u></strong>'),
'#required' => TRUE, '#default_value' => $node->title);
$form['body'] = array('#type' => 'textarea',
'#title' => t('<strong><u>body caption</u></strong>'),
'#default_value' => $node->body, '#rows' => 20, '#required' => TRUE);
$form['format'] = filter_form($node->format);
$form['log'] = array('#type' => 'fieldset', '#title' => t('Log message'),
'#collapsible' => TRUE, '#collapsed' => TRUE);
$form['log']['message'] = array('#type' => 'textarea', '#default_value' => $node->log,
'#description' => t('An explanation of the additions or updates being made to
help other authors understand your motivations.')
);
return $form;
}hook_form using our press release example
function <strong>release</strong>_form(&$node) {
$form['title'] = array('#type' => 'textfield',
'#title' => t('<strong><u>Press Release Title</u></strong>'),
'#required' => TRUE, '#default_value' => $node->title);
$form['body'] = array('#type' => 'textarea',
'#title' => t('<strong><u>Press Release Body</u></strong>'),
'#default_value' => $node->body, '#rows' => 20, '#required' => TRUE);
$form['format'] = filter_form($node->format);
$form['log'] = array('#type' => 'fieldset', '#title' => t('Log message'),
'#collapsible' => TRUE, '#collapsed' => TRUE);
$form['log']['message'] = array('#type' => 'textarea', '#default_value' => $node->log,
'#description' => t('An explanation of the additions or updates being made to
help other authors understand your motivations.')
);
return $form;
}That's it for the code changes. Hopefully not too difficult.
Next
- Save the file, you're now ready to upload
- Create a directory in the modules directory with your technical-name; in our example the directory will be named release
- Upload your new module file to your new directory
- Go to your site, administer -> modules, you should see your new module listed, enable it as with any other module.
Note: if you go to your administer -> modules page and get a white screen, it means you have an error in the php code of your new module, delete the file from your new directory and everything should be fine.
You need to fix your new module, but it is probably easier to start over, re-read the directions and follow them carefully, if it still doesn't work, then consider these directions a bunch of bunk and try something else.
Your new node type should work the same as with page and story nodes, you will need to enable access to them (administer->access control), configure them (administer->settings->content types), allow for categorization (administer->categories), etc.
Sorry for the long directions, I hope they are understandable and usable.

HTML badness
I found it necessary to save this page locally, and search and replace in Emacs the following
<\(/?\)strong>to<\1strong>Then the page looks great!