Index: modules/blog/blog.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/blog/blog.module,v
retrieving revision 1.267
diff -u -p -r1.267 blog.module
--- modules/blog/blog.module	30 Oct 2006 11:21:33 -0000	1.267
+++ modules/blog/blog.module	16 Nov 2006 13:07:48 -0000
@@ -13,7 +13,6 @@ function blog_node_info() {
   return array(
     'blog' => array(
       'name' => t('Blog entry'),
-      'module' => 'blog',
       'description' => t('A blog is a regularly updated journal or diary made up of individual posts shown in reversed chronological order. Each member of the site may create and maintain a blog.'),
     )
   );
Index: modules/book/book.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/book/book.module,v
retrieving revision 1.396
diff -u -p -r1.396 book.module
--- modules/book/book.module	14 Nov 2006 06:30:10 -0000	1.396
+++ modules/book/book.module	16 Nov 2006 13:07:49 -0000
@@ -13,7 +13,6 @@ function book_node_info() {
   return array(
     'book' => array(
       'name' => t('Book page'),
-      'module' => 'book',
       'description' => t("A book is a collaborative writing effort: users can collaborate writing the pages of the book, positioning the pages in the right order, and reviewing or modifying pages previously written. So when you have some information to share or when you read a page of the book and you didn't like it, or if you think a certain page could have been written better, you can do something about it."),
     )
   );
Index: modules/forum/forum.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/forum/forum.module,v
retrieving revision 1.364
diff -u -p -r1.364 forum.module
--- modules/forum/forum.module	14 Nov 2006 06:30:10 -0000	1.364
+++ modules/forum/forum.module	16 Nov 2006 13:07:50 -0000
@@ -114,7 +114,6 @@ function forum_node_info() {
   return array(
     'forum' => array(
       'name' => t('Forum topic'),
-      'module' => 'forum',
       'description' => t('Create a new topic for discussion in the forums.'),
       'title_label' => t('Subject'),
     )
Index: modules/node/content_types.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/content_types.inc,v
retrieving revision 1.12
diff -u -p -r1.12 content_types.inc
--- modules/node/content_types.inc	12 Nov 2006 19:56:07 -0000	1.12
+++ modules/node/content_types.inc	16 Nov 2006 13:07:51 -0000
@@ -17,7 +17,7 @@ function node_overview_types() {
 
   foreach ($names as $key => $name) {
     $type = $types[$key];
-    if (function_exists($type->module .'_form')) {
+    if (function_exists($type->module . _node_type_format_prefix($type->prefix) .'_form')) {
       $name = check_plain($name);
       $type_url_str = str_replace('_', '-', $type->type);
       // Populate the operations field.
@@ -56,7 +56,7 @@ function node_overview_types() {
 function node_type_form($type = NULL) {
   if (!isset($type->type)) {
     $type = new stdClass();
-    $type->type = $type->name = $type->module = $type->description = $type->help = '';
+    $type->type = $type->name = $type->module = $type->prefix = $type->description = $type->help = '';
     $type->min_word_count = 0;
     $type->has_title = TRUE;
     $type->has_body = TRUE;
@@ -175,6 +175,10 @@ function node_type_form($type = NULL) {
     '#type' => 'value',
     '#value' => $type->module,
   );
+  $form['prefix'] = array(
+    '#type' => 'value',
+    '#value' => $type->prefix,
+  );
   $form['custom'] = array(
     '#type' => 'value',
     '#value' => $type->custom,
@@ -269,6 +273,7 @@ function node_type_form_submit($form_id,
   $type->has_body = ($type->body_label != '');
 
   $type->module = !empty($form_values['module']) ? $form_values['module'] : 'node';
+  $type->prefix = (!empty($form_values['prefix']) || $type->module != 'node') ? $form_values['prefix'] : 'content';
   $type->custom = $form_values['custom'];
   $type->modified = TRUE;
   $type->locked = $form_values['locked'];
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.728
diff -u -p -r1.728 node.module
--- modules/node/node.module	16 Nov 2006 08:32:19 -0000	1.728
+++ modules/node/node.module	16 Nov 2006 13:07:53 -0000
@@ -200,8 +200,8 @@ function node_teaser($body, $format = NU
  *
  * @param $op
  *   The format in which to return the list. When this is set to 'type',
- *   'module', or 'name', only the specified node type is returned. When set to
- *   'types' or 'names', all node types are returned.
+ *   'module', 'prefix', or 'name', only the specified node type is returned.
+ *   When set to 'types' or 'names', all node types are returned.
  * @param $node
  *   A node object, array, or string that indicates the node type to return.
  *   Leave at default value (NULL) to return a list of all node types.
@@ -241,6 +241,8 @@ function node_get_types($op = 'types', $
       return $_node_types[$type];
     case 'module':
       return $_node_types[$type]->module;
+    case 'prefix':
+      return $_node_types[$type]->prefix;
     case 'names':
       return $_node_names;
     case 'name':
@@ -281,13 +283,13 @@ function node_type_save($info) {
   $is_existing = db_num_rows(db_query("SELECT * FROM {node_type} WHERE type = '%s'", $existing_type));
 
   if ($is_existing) {
-    db_query("UPDATE {node_type} SET type = '%s', name = '%s', module = '%s', has_title = %d, title_label = '%s', has_body = %d, body_label = '%s', description = '%s', help = '%s', min_word_count = %d, custom = %d, modified = %d, locked = %d WHERE type = '%s'", $info->type, $info->name, $info->module, $info->has_title, $info->title_label, $info->has_body, $info->body_label, $info->description, $info->help, $info->min_word_count, $info->custom, $info->modified, $info->locked, $existing_type);
+    db_query("UPDATE {node_type} SET type = '%s', name = '%s', module = '%s', prefix = '%s', has_title = %d, title_label = '%s', has_body = %d, body_label = '%s', description = '%s', help = '%s', min_word_count = %d, custom = %d, modified = %d, locked = %d WHERE type = '%s'", $info->type, $info->name, $info->module, $info->prefix, $info->has_title, $info->title_label, $info->has_body, $info->body_label, $info->description, $info->help, $info->min_word_count, $info->custom, $info->modified, $info->locked, $existing_type);
 
     module_invoke_all('node_type', 'update', $info);
     return SAVED_UPDATED;
   }
   else {
-    db_query("INSERT INTO {node_type} (type, name, module, has_title, title_label, has_body, body_label, description, help, min_word_count, custom, modified, locked, orig_type) VALUES ('%s', '%s', '%s', %d, '%s', %d, '%s', '%s', '%s', %d, %d, %d, %d, '%s')", $info->type, $info->name, $info->module, $info->has_title, $info->title_label, $info->has_body, $info->body_label, $info->description, $info->help, $info->min_word_count, $info->custom, $info->modified, $info->locked, $info->orig_type);
+    db_query("INSERT INTO {node_type} (type, name, module, prefix, has_title, title_label, has_body, body_label, description, help, min_word_count, custom, modified, locked, orig_type) VALUES ('%s', '%s', '%s', '%s', %d, '%s', %d, '%s', '%s', '%s', %d, %d, %d, %d, '%s')", $info->type, $info->name, $info->module, $info->prefix, $info->has_title, $info->title_label, $info->has_body, $info->body_label, $info->description, $info->help, $info->min_word_count, $info->custom, $info->modified, $info->locked, $info->orig_type);
 
     module_invoke_all('node_type', 'insert', $info);
     return SAVED_NEW;
@@ -331,8 +333,19 @@ function node_type_update_nodes($old_typ
 function _node_types_build() {
   $_node_types = array();
   $_node_names = array();
+  $info_array = array();
+  $hook = 'node_info';
+
+  foreach (module_implements($hook) as $module) {
+    $info = module_invoke($module, $hook);
+    if (isset($info) && is_array($info)) {
+      foreach (array_keys($info) as $type) {
+        $info[$type]['module'] = $module;
+      }
+      $info_array += $info;
+    }
+  }
 
-  $info_array = module_invoke_all('node_info');
   foreach ($info_array as $type => $info) {
     $info['type'] = $type;
     $_node_types[$type] = (object) _node_type_set_defaults($info);
@@ -361,6 +374,10 @@ function _node_types_build() {
  * Set default values for a node type defined through hook_node_info().
  */
 function _node_type_set_defaults($info) {
+  if (!isset($info['prefix'])) {
+    $info['prefix'] = '';
+  }
+
   if (!isset($info['has_title'])) {
     $info['has_title'] = TRUE;
   }
@@ -392,6 +409,27 @@ function _node_type_set_defaults($info) 
 }
 
 /**
+ * Format a node type prefix for use in function calls.
+ */
+function _node_type_format_prefix($prefix, $underscore_before = TRUE) {
+  $ret = '';
+
+  if (empty($prefix)) {
+    return $ret;
+  }
+
+  if ($underscore_before) {
+    $ret .= '_';
+  }
+  $ret .= $prefix;
+  if (!$underscore_before) {
+    $ret .= '_';
+  }
+
+  return $ret;
+}
+
+/**
  * Determine whether a node hook exists.
  *
  * @param &$node
@@ -402,10 +440,10 @@ function _node_type_set_defaults($info) 
  *   TRUE iff the $hook exists in the node type of $node.
  */
 function node_hook(&$node, $hook) {
-  $module = node_get_types('module', $node);
-  if ($module == 'node') {
-    $module = 'node_content'; // Avoid function name collisions.
-  }
+  $type = node_get_types('type', $node);
+  $module = $type->module;
+  $hook = _node_type_format_prefix($type->prefix, FALSE) . $hook;
+
   return module_hook($module, $hook);
 }
 
@@ -423,11 +461,10 @@ function node_hook(&$node, $hook) {
  */
 function node_invoke(&$node, $hook, $a2 = NULL, $a3 = NULL, $a4 = NULL) {
   if (node_hook($node, $hook)) {
-    $module = node_get_types('module', $node);
-    if ($module == 'node') {
-      $module = 'node_content'; // Avoid function name collisions.
-    }
-    $function = $module .'_'. $hook;
+    $type = node_get_types('type', $node);
+    $module = $type->module;
+
+    $function = $module .'_'. _node_type_format_prefix($type->prefix, FALSE) . $hook;
     return ($function($node, $a2, $a3, $a4));
   }
 }
@@ -1123,7 +1160,7 @@ function node_menu($may_cache) {
       'type' => MENU_CALLBACK);
 
     foreach (node_get_types() as $type) {
-      if (function_exists($type->module .'_form')) {
+      if (function_exists($type->module . _node_type_format_prefix($type->prefix) .'_form')) {
         $name = check_plain($type->name);
         $type_url_str = str_replace('_', '-', $type->type);
         $items[] = array(
@@ -2077,7 +2114,7 @@ function node_add($type = NULL) {
   else {
     // If no (valid) node type has been provided, display a node type overview.
     foreach ($types as $type) {
-      if (function_exists($type->module .'_form') && node_access('create', $type->type)) {
+      if (function_exists($type->module . _node_type_format_prefix($type->prefix) .'_form') && node_access('create', $type->type)) {
         $type_url_str = str_replace('_', '-', $type->type);
         $title = t('Add a new @s.', array('@s' => $type->name));
         $out = '<dt>'. l(drupal_ucfirst($type->name), "node/add/$type_url_str", array('title' => $title)) .'</dt>';
@@ -2604,11 +2641,10 @@ function node_access($op, $node = NULL) 
 
   // Can't use node_invoke(), because the access hook takes the $op parameter
   // before the $node parameter.
-  $module = node_get_types('module', $node);
-  if ($module == 'node') {
-    $module = 'node_content'; // Avoid function name collisions.
-  }
-  $access = module_invoke($module, 'access', $op, $node);
+  $type = node_get_types('type', $node);
+  $base = $type->module . _node_type_format_prefix($type->prefix);
+
+  $access = module_invoke($base, 'access', $op, $node);
   if (!is_null($access)) {
     return $access;
   }
Index: modules/poll/poll.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/poll/poll.module,v
retrieving revision 1.219
diff -u -p -r1.219 poll.module
--- modules/poll/poll.module	22 Oct 2006 08:28:45 -0000	1.219
+++ modules/poll/poll.module	16 Nov 2006 13:07:54 -0000
@@ -326,7 +326,6 @@ function poll_node_info() {
   return array(
     'poll' => array(
       'name' => t('Poll'),
-      'module' => 'poll',
       'description' => t("A poll is a multiple-choice question which visitors can vote on."),
       'title_label' => t('Question'),
       'has_body' => FALSE,
Index: modules/system/system.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.install,v
retrieving revision 1.40
diff -u -p -r1.40 system.install
--- modules/system/system.install	8 Nov 2006 19:24:11 -0000	1.40
+++ modules/system/system.install	16 Nov 2006 13:07:57 -0000
@@ -346,6 +346,7 @@ function system_install() {
         type varchar(32) NOT NULL,
         name varchar(255) NOT NULL default '',
         module varchar(255) NOT NULL,
+        prefix varchar(255) NOT NULL,
         description mediumtext NOT NULL,
         help mediumtext NOT NULL,
         has_title tinyint unsigned NOT NULL,
@@ -809,6 +810,7 @@ function system_install() {
         type varchar(32) NOT NULL,
         name varchar(255) NOT NULL default '',
         module varchar(255) NOT NULL,
+        prefix varchar(255) NOT NULL,
         description text NOT NULL,
         help text NOT NULL,
         has_title smallint_unsigned NOT NULL,
@@ -1028,8 +1030,8 @@ function system_install() {
 
   db_query("INSERT INTO {node_access} VALUES (0, 0, 'all', 1, 0, 0)");
 
-  db_query("INSERT INTO {node_type} (type, name, module, description, help, has_title, title_label, has_body, body_label, min_word_count, custom, modified, locked, orig_type) VALUES ('page', 'Page', 'node', 'If you want to add a static page, like a contact page or an about page, use a page.', '', 1, 'Title', 1, 'Body', 0, 1, 1, 0, 'page')");
-  db_query("INSERT INTO {node_type} (type, name, module, description, help, has_title, title_label, has_body, body_label, min_word_count, custom, modified, locked, orig_type) VALUES ('story', 'Story', 'node', '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.', '', 1, 'Title', 1, 'Body', 0, 1, 1, 0, 'story')");
+  db_query("INSERT INTO {node_type} (type, name, module, prefix, description, help, has_title, title_label, has_body, body_label, min_word_count, custom, modified, locked, orig_type) VALUES ('page', 'Page', 'node', 'content', 'If you want to add a static page, like a contact page or an about page, use a page.', '', 1, 'Title', 1, 'Body', 0, 1, 1, 0, 'page')");
+  db_query("INSERT INTO {node_type} (type, name, module, prefix, description, help, has_title, title_label, has_body, body_label, min_word_count, custom, modified, locked, orig_type) VALUES ('story', 'Story', 'node', 'content', '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.', '', 1, 'Title', 1, 'Body', 0, 1, 1, 0, 'story')");
   db_query("INSERT INTO {filter_formats} (name, roles, cache) VALUES ('Filtered HTML',',1,2,',1)");
   db_query("INSERT INTO {filter_formats} (name, roles, cache) VALUES ('PHP code','',0)");
   db_query("INSERT INTO {filter_formats} (name, roles, cache) VALUES ('Full HTML','',1)");
@@ -3173,6 +3175,7 @@ function system_update_1005() {
         type varchar(32) NOT NULL,
         name varchar(255) NOT NULL,
         module varchar(255) NOT NULL,
+        prefix varchar(255) NOT NULL,
         description mediumtext NOT NULL,
         help mediumtext NOT NULL,
         has_title tinyint unsigned NOT NULL,
@@ -3198,6 +3201,7 @@ function system_update_1005() {
         type varchar(32) NOT NULL,
         name varchar(255) NOT NULL,
         module varchar(255) NOT NULL,
+        prefix varchar(255) NOT NULL,
         description text NOT NULL,
         help text NOT NULL,
         has_title smallint_unsigned NOT NULL,
@@ -3220,6 +3224,7 @@ function system_update_1005() {
       'type' => 'page',
       'name' => t('Page'),
       'module' => 'node',
+      'prefix' => 'content',
       'description' => t('If you want to add a static page, like a contact page or an about page, use a page.'),
       'custom' => TRUE,
       'modified' => TRUE,
@@ -3229,6 +3234,7 @@ function system_update_1005() {
       'type' => 'story',
       'name' => t('Story'),
       'module' => 'node',
+      'prefix' => 'content',
       'description' => t('Stories are articles in their simplest form: they have a title, a teaser and a body, but can be extendd by other modules. The teaser is part of the body too. Stories may be used as a personal blog or for news articles.'),
       'custom' => TRUE,
       'modified' => TRUE,
@@ -3375,6 +3381,33 @@ function system_update_1014() {
   return array();
 }
 
+function system_update_1015() {
+  $ret = array();
+  $column_added = FALSE;
+
+  switch ($GLOBALS['db_type']) {
+    case 'mysql':
+    case 'mysqli':
+      if (!db_fetch_object(db_query('DESCRIBE {node_type} prefix'))) {
+        $ret[] = update_sql("ALTER TABLE {node_type} ADD prefix varchar(255) NOT NULL");
+        $column_added = TRUE;
+      }
+      break;
+    case 'pgsql':
+    if (!db_fetch_object(db_query("SELECT column_name FROM information_schema.columns WHERE table_name = '{node_type}' AND column_name = 'prefix';")))
+      db_add_column($ret, 'node_type', 'prefix', 'varchar(255)', array('not null' => TRUE));
+        $column_added = TRUE;
+      break;
+  }
+
+  if ($column_added) {
+    db_query("UPDATE {node_type} SET prefix = 'content' WHERE module = 'node'");
+    node_types_rebuild();
+  }
+
+  return $ret;
+}
+
 /**
  * @} End of "defgroup updates-4.7-to-x.x"
  * The next series of updates should start at 2000.
