=== removed file 'modules/page.module'
--- modules/page.module	
+++ /dev/null	
@@ -1,100 +0,0 @@
-<?php
-// $Id: page.module,v 1.155 2006/05/07 00:08:36 drumm Exp $
-
-/**
- * @file
- * Enables the creation of pages that can be added to the navigation system.
- */
-
-/**
- * Implementation of hook_help().
- */
-function page_help($section) {
-  switch ($section) {
-    case 'admin/help#page':
-      $output = '<p>'. t('The page module allows users to create static pages, which are the most basic type of content. Pages are commonly collected in books via the book module. Users should create a page if the information on the page is static. An example would be an "about" page. ') .'</p>';
-      $output .= '<p>'. t('When a  page is created, a user can set authoring information, configure publishing options, whether readers will be able to post comments. They can also select the content type of the page (e.g., full HTML, filtered HTML). ') .'</p>';
-      $output .= '<p>'. t('As an administrator, you can set the publishing default for a page (in its workflow): you can specify whether a page is by default published, sent to moderation, promoted to the front page, sticky at the top of lists, and whether revisions are enabled by default. You can set the permissions that different user roles have to view, create, and edit pages.') .'</p>';
-      $output .= '<p>'. t('If the location module is enabled, then location specific information can be added. If the trackback module is enabled trackbacks can be configured.') .'</p>';
-      $output .= t('<p>You can</p>
-<ul>
-<li>read the node administration help at <a href="%admin-help-node">administer &gt;&gt; help &gt;&gt; node</a>.</li>
-<li>read the page administration help at <a href="%admin-help-page">administer &gt;&gt; help &gt;&gt; page</a>.</li>
-<li>read the story administration help at <a href="%admin-help-story">administer &gt;&gt; help &gt;&gt; story</a>.</li>
-<li>create a page at <a href="%node-add-page">create content &gt;&gt; page</a>.</li>
-<li>administer page content type at <a href="%admin-settings-content-types-page">administer &gt;&gt; settings &gt;&gt; content types &gt;&gt; configure page</a>.</li>
-</ul>
-', array('%admin-help-node' => url('admin/help/node'), '%admin-help-page' => url('admin/help/page'), '%admin-help-story' => url('admin/help/story'), '%node-add-page' => url('node/add/page'), '%admin-settings-content-types-page' => url('admin/settings/content-types/page')));
-      $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%page">Page page</a>.', array('%page' => 'http://drupal.org/handbook/modules/page/')) .'</p>';
-      return $output;
-    case 'admin/modules#description':
-      return t('Enables the creation of pages that can be added to the navigation system.');
-    case 'node/add#page':
-      return t('If you want to add a static page, like a contact page or an about page, use a page.');
-  }
-}
-
-/**
- * Implementation of hook_perm().
- */
-function page_perm() {
-  return array('create pages', 'edit own pages');
-}
-
-/**
- * Implementation of hook_node_info().
- */
-function page_node_info() {
-  return array('page' => array('name' => t('page'), 'base' => 'page'));
-}
-
-/**
- * Implementation of hook_access().
- */
-function page_access($op, $node) {
-  global $user;
-
-  if ($op == 'create') {
-    return user_access('create pages');
-  }
-
-  if ($op == 'update' || $op == 'delete') {
-    if (user_access('edit own pages') && ($user->uid == $node->uid)) {
-      return TRUE;
-    }
-  }
-}
-
-/**
- * Implementation of hook_menu().
- */
-function page_menu($may_cache) {
-  $items = array();
-
-  if ($may_cache) {
-    $items[] = array('path' => 'node/add/page', 'title' => t('page'),
-      'access' => user_access('create pages'));
-  }
-
-  return $items;
-}
-
-/**
- * Implementation of hook_form().
- */
-function page_form(&$node) {
-
-  $form['title'] = array('#type' => 'textfield', '#title' => t('Title'), '#required' => TRUE, '#default_value' => $node->title, '#weight' => -5);
-
-  $form['body_filter']['body'] = array('#type' => 'textarea', '#title' => t('Body'), '#default_value' => $node->body, '#rows' => 20, '#required' => TRUE);
-  $form['body_filter']['format'] = filter_form($node->format);
-
-  $form['log'] = array(
-    '#type' => 'textarea', '#title' => t('Log message'), '#default_value' => $node->log, '#weight' => 5,
-    '#description' => t('An explanation of the additions or updates being made to help other authors understand your motivations.')
-  );
-
-  return $form;
-}
-
-
=== removed file 'modules/story.module'
--- modules/story.module	
+++ /dev/null	
@@ -1,86 +0,0 @@
-<?php
-// $Id: story.module,v 1.187 2006/05/07 00:08:36 drumm Exp $
-
-/**
- * @file
- * Enables users to submit stories, articles or similar content.
- */
-
-/**
- * Implementation of hook_help().
- */
-function story_help($section) {
-  switch ($section) {
-    case 'admin/help#story':
-      $output = '<p>'. t('The story module is used to create a content post type called <em>stories.</em> 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. ') .'</p>';
-      $output .= '<p>'. t('The story 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 story at <a href="%node-add-story">create content &gt;&gt; story</a>.</li>
-<li>configure story at <a href="%admin-settings-content-types-story">administer &gt;&gt; settings &gt;&gt; content types &gt;&gt; configure story</a>.</li>
-</ul>
-', array('%node-add-story' => url('node/add/story'), '%admin-settings-content-types-story' => url('admin/settings/content-types/story')));
-      $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%story">Story page</a>.', array('%story' => 'http://drupal.org/handbook/modules/story/')) .'</p>';
-      return $output;
-    case 'admin/modules#description':
-      return t('Allows users to submit stories, articles or similar content.');
-    case 'node/add#story':
-      return t('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.');
-  }
-}
-
-/**
- * Implementation of hook_node_info().
- */
-function story_node_info() {
-  return array('story' => array('name' => t('story'), 'base' => 'story'));
-}
-
-/**
- * Implementation of hook_perm().
- */
-function story_perm() {
-  return array('create stories', 'edit own stories');
-}
-
-/**
- * Implementation of hook_access().
- */
-function story_access($op, $node) {
-  global $user;
-
-  if ($op == 'create') {
-    return user_access('create stories');
-  }
-
-  if ($op == 'update' || $op == 'delete') {
-    if (user_access('edit own stories') && ($user->uid == $node->uid)) {
-      return TRUE;
-    }
-  }
-}
-
-/**
- * Implementation of hook_menu().
- */
-function story_menu($may_cache) {
-  $items = array();
-
-  if ($may_cache) {
-    $items[] = array('path' => 'node/add/story', 'title' => t('story'),
-      'access' => user_access('create stories'));
-  }
-
-  return $items;
-}
-
-/**
- * Implementation of hook_form().
- */
-function story_form(&$node) {
-  $form['title'] = array('#type' => 'textfield', '#title' => t('Title'), '#required' => TRUE, '#default_value' => $node->title, '#weight' => -5);
-  $form['body_filter']['body'] = array('#type' => 'textarea', '#title' => t('Body'), '#default_value' => $node->body, '#rows' => 20, '#required' => TRUE);
-  $form['body_filter']['format'] = filter_form($node->format);
-  return $form;
-}
-
=== added file 'modules/content.module'
--- /dev/null	
+++ modules/content.module	
@@ -0,0 +1,404 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Allows administrators to define new content types.
+ */
+
+define('CONTENT_TYPE_PREFIX', 'content-');
+
+/**
+ * Implementation of hook_help().
+ */
+function content_help($section) {
+  switch ($section) {
+    case 'admin/modules#description':
+      return t('Allows administrators to define new content types.');
+  }
+}
+
+/**
+ * Implementation of hook_perm().
+ */
+function content_perm() {
+  foreach (node_get_types() as $type) {
+    if ($type->base == 'content') {
+      $name = check_plain($type->name);
+      $perms[] = "create $name content";
+      $perms[] = "edit own $name content";
+      $perms[] = "edit $name content";
+    }
+  }
+  return $perms;
+}
+
+/**
+ * Implementation of hook_menu().
+ */
+function content_menu($may_cache) {
+  $items = array();
+
+  if ($may_cache) {
+    $items[] = array(
+      'path' => 'admin/node/types',
+      'title' => t('content types'),
+      'callback' => 'content_types_page',
+      'access' => user_access('administer nodes'),
+    );
+    $items[] = array(
+      'path' => 'admin/node/types/list',
+      'title' => t('list'),
+      'type' => MENU_DEFAULT_LOCAL_TASK,
+      'weight' => -10,
+    );
+    $items[] = array(
+      'path' => 'admin/node/types/add',
+      'title' => t('add content type'),
+      'callback' => 'content_type_form',
+      'type' => MENU_LOCAL_TASK,
+    );
+  }
+  else {
+    if (arg(0) == 'admin' && arg(1) == 'node' && arg(2) == 'types' && arg(3)) {
+
+      $type = node_get_type(arg(3));
+
+      if ($type) {
+        $type->name = check_plain($type->name);
+        $items[] = array(
+          'path' => 'admin/node/types/'. arg(3),
+          'title' => t($type->name),
+          'callback' => 'content_type_form',
+          'callback arguments' => array($type),
+          'type' => MENU_CALLBACK,
+        );
+        $items[] = array(
+          'path' => 'admin/node/types/'. arg(3) .'/delete',
+          'title' => t('delete'),
+          'callback' => 'content_type_delete',
+          'callback arguments' => array($type),
+          'type' => MENU_CALLBACK,
+        );
+      }
+    }
+  }
+
+  return $items;
+}
+
+/**
+ * Implementation of hook_node_info().
+ */
+function content_node_info() {
+  return array(
+    'content-page' => array(
+      'name' => t('page'),
+      'base' => 'content',
+      'description' => t('If you want to add a static page, like a contact page or an about page, use a page.'),
+      'status' => NODE_TYPE_NORMAL | NODE_TYPE_MODIFIABLE_BY_ADMIN
+    ),
+    'content-story' => array(
+      'name' => t('story'),
+      'base' => 'content',
+      'description' => t('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.'),
+      'status' => NODE_TYPE_NORMAL | NODE_TYPE_MODIFIABLE_BY_ADMIN
+    )
+  );
+}
+
+/**
+ * Implementation of hook_access().
+ */
+function content_access($op, $node) {
+  global $user;
+  $type = is_string($node) ? $node : (is_array($node) ? $node['type'] : $node->type);
+
+  if ($op == 'create') {
+    return user_access('create '. $type .' content');
+  }
+
+  if ($op == 'update' || $op == 'delete') {
+    if (user_access('edit '. $type .' content') || (user_access('edit own '. $type .' content') && ($user->uid == $node->uid))) {
+      return TRUE;
+    }
+  }
+}
+
+/**
+ * Implementation of hook_form().
+ *
+ * Each field defines its own component of the content entry form, via its
+ * chosen widget.
+ */
+function content_form($node) {
+  $type = node_get_type($node);
+
+  if (!empty($type->title_label)) {
+    $form['title'] = array(
+      '#type' => 'textfield',
+      '#title' => check_plain(t($type->title_label)),
+      '#required' => TRUE,
+      '#default_value' => $node->title,
+      '#weight' => -5,
+    );
+  }
+
+  if (!empty($type->body_label)) {
+    $form['body_filter']['body'] = array(
+      '#type' => 'textarea',
+      '#title' => check_plain(t($type->body_label)),
+      '#default_value' => $node->body,
+      '#rows' => 20,
+      '#required' => TRUE);
+    $form['body_filter']['format'] = filter_form($node->format);
+  }
+
+
+  return $form;
+}
+
+function content_types_page($type = NULL) {
+  if (isset($type)) {
+    $type = node_get_type($type);
+    if (isset($type)) {
+      return content_type_form($type);
+    }
+  }
+
+  return content_overview_types();
+}
+
+function content_overview_types() {
+  node_types_rebuild();
+  menu_rebuild();
+
+  $types = node_get_types();
+  $header = array(t('Name'), t('Type'), t('Description'), array('data' => t('Operations'), 'colspan' => '3'));
+  $rows = array();
+
+  foreach ($types as $type) {
+    if ($type->status & NODE_TYPE_VISIBLE_IN_ADMIN_LIST) {
+      $name = check_plain($type->name);
+      // Populate the operations field.
+      $operations = array();
+
+      if (!(($type->status & NODE_TYPE_MODIFIABLE_BY_ADMIN) || ($type->status & NODE_TYPE_TOGGLEABLE_BY_ADMIN))) {
+        $operations[] = array('data' => t('locked'), 'colspan' => '3', 'align' => 'center');
+      }
+      else {
+        if (($type->status & NODE_TYPE_MODIFIABLE_BY_ADMIN) && ($type->status & NODE_TYPE_ENABLED)) {
+          // Set the edit column.
+          $operations[] = array('data' => l(t('edit'), 'admin/node/types/'. $type->type));
+        }
+        else {
+          $operations[] = array('data' => '');
+        }
+
+        if ($type->status & NODE_TYPE_TOGGLEABLE_BY_ADMIN) {
+          // Set the disable column.
+          if ($type->status & NODE_TYPE_ENABLED) {
+            $operations[] = array('data' => l(t('disable'), 'admin/node/types/'. $type->type .'/disable'));
+          }
+          else {
+            $operations[] = array('data' => l(t('enable'), 'admin/node/types/'. $type->type));
+          }
+        }
+        else {
+          $operations[] = array('data' => '');
+        }
+
+        // Set the reset column.
+        if ($type->status & NODE_TYPE_CREATED_BY_ADMIN) {
+          $operations[] = array('data' => l(t('delete'), 'admin/node/types/'. $type->type .'/delete'));
+        }
+        else if ($type->status & NODE_TYPE_MODIFIED_BY_ADMIN) {
+          $operations[] = array('data' => l(t('reset'), 'admin/node/types/'. $type->type .'/reset'));
+        }
+        else {
+          $operations[] = array('data' => '');
+        }
+      }
+
+      // Call out disabled items.
+      if ($type->status & NODE_TYPE_ENABLED) {
+        $class = 'item-enabled';
+      }
+      else {
+        $name .= ' ('. t('disabled') .')';
+        $class = 'item-disabled';
+      }
+
+      $row = array(array('data' => $name, 'class' => $class), array('data' => check_plain($type->type), 'class' => $class), array('data' => check_plain($type->description), 'class' => $class));
+      foreach ($operations as $operation) {
+        $operation['class'] = $class;
+        $row[] = $operation;
+      }
+      $rows[] = $row;
+    }
+  }
+
+  if (empty($rows)) {
+    $rows[] = array('data' => t('No content types available.'), 'colspan' => '6', 'class' => 'message');
+  }
+
+  return theme('table', $header, $rows);
+}
+
+function content_type_form($type = NULL) {
+  if (!isset($type)) {
+    $type = new stdClass();
+    $type->type = $type->name = $type->description = $type->help = '';
+    $type->has_title = TRUE;
+    $type->has_body = TRUE;
+    $type->title_label = t('Title');
+    $type->body_label = t('Body');
+  }
+  else {
+    // Chop off the non-user-defined prefix from the content type string.
+    $type->type = drupal_substr($type->type, drupal_strlen(CONTENT_TYPE_PREFIX));
+  }
+
+  $form['name'] = array(
+    '#title' => t('Name'),
+    '#type' => 'textfield',
+    '#default_value' => $type->name,
+    '#description' => t('The human-readable name of this content type. The name must be unique to this content type.'),
+    '#required' => TRUE,
+  );
+  $form['type'] = array(
+    '#title' => t('Type'),
+    '#type' => 'textfield',
+    '#default_value' => $type->type,
+    '#maxlength' => (32 - drupal_strlen(CONTENT_TYPE_PREFIX)),
+    '#description' => t('The machine-readable name of this content type. The text that you enter here will be prefixed by %content when used by the system. Only lowercase letters, numbers, and underscores are allowed. The name must be unique to this content type. Leave blank to have this automatically generated, based on the content type name.', array('%content' => theme('placeholder', CONTENT_TYPE_PREFIX))),
+  );
+  $form['description'] = array(
+    '#title' => t('Description'),
+    '#type' => 'textarea',
+    '#default_value' => $type->description,
+    '#rows' => 10,
+    '#description' => t('A brief description of this content type.'),
+  );
+  $form['help'] = array(
+    '#title' => t('Help text'),
+    '#type' => 'textarea',
+    '#default_value' => $type->help,
+    '#rows' => 10,
+    '#description' => t('Instructions to present to the user when adding new content of this type.'),
+  );
+  if ($type->has_title) {
+    $form['title_label'] = array(
+      '#title' => t('Title field label'),
+      '#type' => 'textfield',
+      '#default_value' => $type->title_label,
+      '#description' => t('The label for the title field of this content type.'),
+      '#required' => TRUE,
+    );
+  }
+  if ($type->has_body) {
+    $form['body_label'] = array(
+      '#title' => t('Body field label'),
+      '#type' => 'textfield',
+      '#default_value' => $type->body_label,
+      '#description' => t('The label for the body field of this content type.'),
+      '#required' => TRUE,
+    );
+  }
+
+  if (!empty($type->type)) {
+    $form['orig_type'] = array(
+      '#type' => 'value',
+      '#value' => $type->type,
+    );
+  }
+
+  $form['submit'] = array(
+    '#value' => t('Save content type'),
+    '#type' => 'submit',
+  );
+
+  return drupal_get_form('content_type_form', $form);
+}
+
+function content_type_form_validate($form_id, $form_values) {
+  if (!empty($form_values['type']) && !preg_match('/^[a-z0-9_]+$/', $form_values['type'])) {
+    form_set_error('type', t('The machine-readable name of this content type may only consist of lowercase letters, numbers, and underscores.'));
+  }
+
+  $types = node_get_types();
+  $type = new stdClass();
+  _content_type_set_name_and_type($type, $form_values);
+  if (isset($types[$type->type]) && (!isset($form_values['orig_type']) || $form_values['orig_type'] != $form_values['type'])) {
+    form_set_error('type', t('The machine-readable name %type is already taken.', array('%type' => $form_values['type'])));
+  }
+
+  foreach ($types as $info) {
+    if ($info->name == $form_values['name'] && (!isset($form_values['orig_type']) || $form_values['orig_type'] != $form_values['type'])) {
+      form_set_error('name', t('The human-readable name %name is already taken.', array('%name' => $form_values['name'])));
+    }
+  }
+}
+
+function content_type_form_submit($form_id, $form_values) {
+  $type = new stdClass();
+
+  _content_type_set_name_and_type($type, $form_values);
+
+  $type->description = $form_values['description'];
+  $type->help = $form_values['help'];
+  $type->title_label = $form_values['title_label'];
+  $type->body_label = $form_values['body_label'];
+
+  $type->base = drupal_substr(CONTENT_TYPE_PREFIX, 0, (drupal_strlen(CONTENT_TYPE_PREFIX) - 1));
+  $type->has_title = $type->has_body = TRUE;
+  $type->status = NODE_TYPE_CUSTOM | NODE_TYPE_MODIFIED_BY_ADMIN;
+  $type->status |= NODE_TYPE_VISIBLE_IN_ADMIN_LIST;
+
+  if (isset($form_values['orig_type'])) {
+    $orig_type = CONTENT_TYPE_PREFIX . $form_values['orig_type'];
+
+    if ($orig_type != $type->type) {
+      $update_count = node_type_update_nodes($orig_type, $type->type);
+
+      if ($update_count) {
+        $t_args = array('%update_count' => theme('placeholder', $update_count), '%orig_type' => theme('placeholder', $orig_type), '%type' => theme('placeholder', $type->type));
+        drupal_set_message(t('Changed the content type of %update_count posts from %orig_type to %type.', $t_args));
+        cache_clear_all('filter:', TRUE);
+      }
+    }
+  }
+
+  $status = node_type_save($type);
+
+  $t_args = array('%name' => theme('placeholder', $type->name));
+  if ($status == SAVED_UPDATED) {
+    drupal_set_message(t('The content type %name has been updated.', $t_args));
+  }
+  elseif ($status == SAVED_NEW) {
+    drupal_set_message(t('The content type %name has been added.', $t_args));
+    watchdog('content', t('Added content type %name.', $t_args), WATCHDOG_NOTICE, l(t('view'), 'admin/node/types'));
+  }
+
+  node_types_rebuild();
+  menu_rebuild();
+
+  return 'admin/node/types';
+}
+
+function _content_type_set_name_and_type(&$type, $values) {
+  $type->name = trim($values['name']);
+  $type->type = trim($values['type']);
+
+  if (empty($type->type)) {
+    $type->type = $type->name;
+    $type->type = drupal_strtolower($type->type);
+    $type->type = str_replace(array(' ', '-'), '_', $type->type);
+    $type->type = preg_replace('/[^a-z0-9_]/', '', $type->type);
+    $type->type = drupal_substr($type->type, 0, 32);
+  }
+
+  $type->type = CONTENT_TYPE_PREFIX . $type->type;
+  $type->type = drupal_substr($type->type, 0, 32);
+}
+
Index: database/database.4.0.mysql
===================================================================
RCS file: /cvs/drupal/drupal/database/database.4.0.mysql,v
retrieving revision 1.5
diff -u -r1.5 database.4.0.mysql
--- database/database.4.0.mysql	29 May 2006 16:04:41 -0000	1.5
+++ database/database.4.0.mysql	18 Jun 2006 14:22:56 -0000
@@ -460,6 +460,24 @@
 );
 
 --
+-- Table structure for table 'node_type'
+--
+
+CREATE TABLE node_type (
+  type varchar(255) NOT NULL,
+  name varchar(255) NOT NULL default '',
+  base varchar(255) NOT NULL,
+  description mediumtext NOT NULL,
+  help mediumtext NOT NULL,
+  has_title tinyint(3) unsigned NOT NULL,
+  title_label varchar(255) NOT NULL default '',
+  has_body tinyint(3) unsigned NOT NULL,
+  body_label varchar(255) NOT NULL default '',
+  status int(2) unsigned NOT NULL default '0',
+  PRIMARY KEY (type)
+);
+
+--
 -- Table structure for table 'profile_fields'
 --
 
Index: database/database.4.1.mysql
===================================================================
RCS file: /cvs/drupal/drupal/database/database.4.1.mysql,v
retrieving revision 1.5
diff -u -r1.5 database.4.1.mysql
--- database/database.4.1.mysql	29 May 2006 16:04:41 -0000	1.5
+++ database/database.4.1.mysql	18 Jun 2006 14:22:59 -0000
@@ -491,6 +491,25 @@
 DEFAULT CHARACTER SET utf8;
 
 --
+-- Table structure for table 'node_type'
+--
+
+CREATE TABLE node_type (
+  type varchar(255) NOT NULL default '',
+  name varchar(255) NOT NULL,
+  base varchar(255) NOT NULL,
+  description mediumtext NOT NULL,
+  help mediumtext NOT NULL,
+  has_title tinyint(3) unsigned NOT NULL,
+  title_label varchar(255) NOT NULL default '',
+  has_body tinyint(3) unsigned NOT NULL,
+  body_label varchar(255) NOT NULL default '',
+  status int(2) unsigned NOT NULL default '0',
+  PRIMARY KEY (type)
+)
+DEFAULT CHARACTER SET utf8;
+
+--
 -- Table structure for table 'profile_fields'
 --
 
Index: database/database.pgsql
===================================================================
RCS file: /cvs/drupal/drupal/database/database.pgsql,v
retrieving revision 1.178
diff -u -r1.178 database.pgsql
--- database/database.pgsql	29 May 2006 16:04:41 -0000	1.178
+++ database/database.pgsql	18 Jun 2006 14:23:02 -0000
@@ -468,6 +468,24 @@
 CREATE SEQUENCE node_revisions_vid_seq INCREMENT 1 START 1;
 
 --
+-- Table structure for table 'node_type'
+--
+
+CREATE TABLE node_type (
+  type varchar(255) NOT NULL default '',
+  name varchar(255) NOT NULL,
+  base varchar(255) NOT NULL,
+  description text NOT NULL,
+  help text NOT NULL,
+  has_title integer unsigned NOT NULL,
+  title_label varchar(255) NOT NULL default '',
+  has_body integer unsigned NOT NULL,
+  body_label varchar(255) NOT NULL default '',
+  status int(2) unsigned NOT NULL default '0',
+  PRIMARY KEY (type)
+);
+
+--
 -- Table structure for table 'url_alias'
 --
 
Index: database/updates.inc
===================================================================
RCS file: /cvs/drupal/drupal/database/updates.inc,v
retrieving revision 1.237
diff -u -r1.237 updates.inc
--- database/updates.inc	11 Jun 2006 23:10:13 -0000	1.237
+++ database/updates.inc	18 Jun 2006 14:23:10 -0000
@@ -2071,3 +2071,47 @@
   return $ret;
 }
 
+function system_update_186() {
+  // Add ability to create dynamic node types like the CCK module
+  $ret = array();
+
+  switch ($GLOBALS['db_type']) {
+    case 'mysqli':
+    case 'mysql':
+      // Create node_type table
+      $ret[] = update_sql("CREATE TABLE {node_type} (
+        type varchar(255) NOT NULL,
+        name varchar(255) NOT NULL,
+        base varchar(255) NOT NULL,
+        description mediumtext NOT NULL,
+        help mediumtext NOT NULL,
+        has_title tinyint(3) unsigned NOT NULL,
+        title_label varchar(255) NOT NULL default '',
+        has_body tinyint(3) unsigned NOT NULL,
+        body_label varchar(255) NOT NULL default '',
+        status int(2) unsigned NOT NULL default '0',
+        PRIMARY KEY (type)
+        ) /*!40100 DEFAULT CHARACTER SET utf8 */;");
+      break;
+
+    case 'pgsql':
+      $ret[] = update_sql("CREATE TABLE {node_type} (
+        type varchar(255) NOT NULL,
+        name varchar(255) NOT NULL,
+        base varchar(255) NOT NULL,
+        description text NOT NULL,
+        help text NOT NULL,
+        has_title integer unsigned NOT NULL,
+        title_label varchar(255) NOT NULL default '',
+        has_body integer unsigned NOT NULL,
+        body_label varchar(255) NOT NULL default '',
+        status int(2) unsigned NOT NULL default '0',
+        PRIMARY KEY (type)
+        );");
+      break;
+  }
+
+  node_types_rebuild();
+
+  return $ret;
+}
Index: misc/drupal.css
===================================================================
RCS file: /cvs/drupal/drupal/misc/drupal.css,v
retrieving revision 1.150
diff -u -r1.150 drupal.css
--- misc/drupal.css	23 May 2006 02:11:17 -0000	1.150
+++ misc/drupal.css	18 Jun 2006 14:23:11 -0000
@@ -72,9 +72,6 @@
 li a.active {
   color: #000;
 }
-td.menu-disabled {
-  background: #ccc;
-}
 
 /*
 ** Other common styles
@@ -128,6 +125,9 @@
   padding: 0;
   list-style: disc;
 }
+td.item-disabled {
+  background: #ccc;
+}
 .form-item {
   margin-top: 1em;
   margin-bottom: 1em;
Index: modules/book.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/book.module,v
retrieving revision 1.368
diff -u -r1.368 book.module
--- modules/book.module	5 Jun 2006 09:00:22 -0000	1.368
+++ modules/book.module	18 Jun 2006 14:23:16 -0000
@@ -10,7 +10,13 @@
  * Implementation of hook_node_info().
  */
 function book_node_info() {
-  return array('book' => array('name' => t('book page'), 'base' => 'book'));
+  return array(
+    'book' => array(
+      'name' => t('book page'),
+      'base' => '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."),
+    )
+  );
 }
 
 /**
@@ -84,10 +90,6 @@
 
   if ($may_cache) {
     $items[] = array(
-      'path' => 'node/add/book',
-      'title' => t('book page'),
-      'access' => user_access('create book pages'));
-    $items[] = array(
       'path' => 'admin/node/book',
       'title' => t('books'),
       'callback' => 'book_admin',
@@ -1019,8 +1021,6 @@
       return t('<p>The book module offers a means to organize content, authored by many users, in an online manual, outline or FAQ.</p>');
     case 'admin/node/book/orphan':
       return t('<p>Pages in a book are like a tree. As pages are edited, reorganized and removed, child pages might be left with no link to the rest of the book. Such pages are referred to as "orphan pages". On this page, administrators can review their books for orphans and reattach those pages as desired.</p>');
-    case 'node/add#book':
-      return 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.");
   }
 
   if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == 'outline') {
Index: modules/menu.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/menu.module,v
retrieving revision 1.74
diff -u -r1.74 menu.module
--- modules/menu.module	7 May 2006 00:08:36 -0000	1.74
+++ modules/menu.module	18 Jun 2006 14:23:20 -0000
@@ -683,7 +683,7 @@
       }
       else {
         $title .= ' ('. t('disabled') .')';
-        $class = 'menu-disabled';
+        $class = 'item-disabled';
       }
 
       if ($item['type'] & (MENU_MODIFIABLE_BY_ADMIN | MENU_VISIBLE_IN_TREE)) {
Index: modules/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node.module,v
retrieving revision 1.651
diff -u -r1.651 node.module
--- modules/node.module	16 Jun 2006 20:16:27 -0000	1.651
+++ modules/node.module	18 Jun 2006 14:23:30 -0000
@@ -8,6 +8,43 @@
 
 define('NODE_NEW_LIMIT', time() - 30 * 24 * 60 * 60);
 
+define('NODE_TYPE_ENABLED', 0x0001);
+define('NODE_TYPE_MODIFIABLE_BY_ADMIN', 0x0002);
+define('NODE_TYPE_MODIFIED_BY_ADMIN', 0x0004);
+define('NODE_TYPE_TOGGLEABLE_BY_ADMIN', 0x0008);
+define('NODE_TYPE_TOGGLED_BY_ADMIN', 0x0010);
+define('NODE_TYPE_CREATED_BY_ADMIN', 0x0020);
+define('NODE_TYPE_IS_CONTENT', 0x0040);
+define('NODE_TYPE_VISIBLE_IN_ADMIN_LIST', 0x0080);
+
+/**
+ * Normal node types are defined by modules, and can be disabled (but not
+ * edited) by the administrator. Use this for most node types that are
+ * non-user-defined, and that are designed to be used for creating content. It
+ * is the default value if no node type status is specified.
+ */
+define('NODE_TYPE_NORMAL', NODE_TYPE_ENABLED | NODE_TYPE_TOGGLEABLE_BY_ADMIN | NODE_TYPE_IS_CONTENT);
+
+/**
+ * Suggested node types are just like normal node types, except that they are
+ * disabled by default.
+ */
+define('NODE_TYPE_SUGGESTED', NODE_TYPE_TOGGLEABLE_BY_ADMIN | NODE_TYPE_IS_CONTENT);
+
+/**
+ * System node types are also defined by modules, but they cannot be disabled
+ * by the administrator (although they are still shown in the node type
+ * administration list). Use this for most node types that are non-user-
+ * defined, and that are not designed to be used for creating content.
+ */
+define('NODE_TYPE_SYSTEM', NODE_TYPE_ENABLED | NODE_TYPE_VISIBLE_IN_ADMIN_LIST);
+
+/**
+ * Custom node types are those defined by the administrator. Reserved for
+ * internal use; do not return from hook_node_info() implementations.
+ */
+define('NODE_TYPE_CUSTOM', NODE_TYPE_ENABLED | NODE_TYPE_CREATED_BY_ADMIN | NODE_TYPE_MODIFIABLE_BY_ADMIN | NODE_TYPE_TOGGLEABLE_BY_ADMIN | NODE_TYPE_IS_CONTENT);
+
 /**
  * Implementation of hook_help().
  */
@@ -48,7 +85,8 @@
   }
 
   if (arg(0) == 'node' && arg(1) == 'add' && $type = arg(2)) {
-    return filter_xss_admin(variable_get($type .'_help', ''));
+    $type = node_get_type(arg(2));
+    return filter_xss_admin($type->help);
   }
 }
 
@@ -195,16 +233,22 @@
   return truncate_utf8($body, $size);
 }
 
-function _node_names($op = '', $node = NULL) {
-  static $node_names = array();
-  static $node_list = array();
-
-  if (empty($node_names)) {
-    $node_names = module_invoke_all('node_info');
-    foreach ($node_names as $type => $value) {
-      $node_list[$type] = $value['name'];
+function _node_types($op = '', $node = NULL, $reset = FALSE) {
+  global $_node_types, $_node_names;
+
+  if ($reset || !isset($_node_types)) {
+    if ($cached = cache_get('node_types')) {
+      $_node_types = unserialize($cached->data);
+      $cached = cache_get('node_names');
+      $_node_names = unserialize($cached->data);
+    }
+    else {
+      _node_types_build();
+      cache_set('node_types', serialize($_node_types), CACHE_PERMANENT);
+      cache_set('node_names', serialize($_node_names), CACHE_PERMANENT);
     }
   }
+
   if ($node) {
     if (is_array($node)) {
       $type = $node['type'];
@@ -215,21 +259,138 @@
     elseif (is_string($node)) {
       $type = $node;
     }
-    if (!isset($node_names[$type])) {
+    if (!isset($_node_types[$type])) {
       return FALSE;
     }
   }
   switch ($op) {
+    case 'types':
+      return $_node_types;
+    case 'type':
+      return $_node_types[$type];
     case 'base':
-      return $node_names[$type]['base'];
-    case 'list':
-      return $node_list;
+      return $_node_types[$type]->base;
+    case 'names':
+      return $_node_names;
     case 'name':
-      return $node_list[$type];
+      return $_node_names[$type];
+  }
+}
+
+function node_types_rebuild() {
+  cache_clear_all('node_types');
+
+  _node_types_build();
+
+  $node_types = _node_types('types');
+
+  foreach ($node_types as $type => $info) {
+    if (!empty($info->is_new)) {
+      node_type_save($info);
+    }
+  }
+
+  _node_types_build();
+}
+
+function node_type_save($info) {
+  $is_existing = db_num_rows(db_query("SELECT * FROM {node_type} WHERE type = '%s'", $info->type));
+
+  if ($is_existing) {
+    db_query("UPDATE {node_type} SET name = '%s', base = '%s', description = '%s', help = '%s', has_title = %d, title_label = '%s', has_body = %d, body_label = '%s', status = %d WHERE type = '%s'", $info->name, $info->base, $info->description, $info->help, $info->has_title, $info->title_label, $info->has_body, $info->body_label, $info->status, $info->type);
+    return SAVED_UPDATED;
+  }
+  else {
+    db_query("INSERT INTO {node_type} (type, name, base, description, help, has_title, title_label, has_body, body_label, status) VALUES ('%s', '%s', '%s', '%s', '%s', %d, '%s', %d, '%s', %d)", $info->type, $info->name, $info->base, $info->description, $info->help, $info->has_title, $info->title_label, $info->has_body, $info->body_label, $info->status);
+    return SAVED_NEW;
+  }
+}
+
+function node_type_update_nodes($orig_type, $type) {
+  return db_num_rows(db_query("UPDATE {node} SET type = '%s' WHERE type = '%s'", $type, $orig_type));
+}
+
+function _node_types_build() {
+  global $_node_types, $_node_names;
+
+  $_node_types = array();
+  $_node_names = array();
+
+  $info_array = module_invoke_all('node_info');
+  foreach ($info_array as $type => $info) {
+    $info['type'] = $type;
+    $_node_types[$type] = _node_type_set_defaults($info);
+    $_node_names[$type] = $info['name'];
+  }
+
+  $type_result = db_query(db_rewrite_sql('SELECT nt.type, nt.* FROM {node_type} nt ORDER BY nt.type ASC', 'nt', 'type'));
+  while ($type_object = db_fetch_object($type_result)) {
+    if (module_exist('field')) {
+      $type_object->fields = field_load_type($type_object->type);
+    }
+    if (!isset($_node_types[$type_object->type]) || ($type_object->status & NODE_TYPE_MODIFIED_BY_ADMIN) || ($type_object->status & NODE_TYPE_TOGGLED_BY_ADMIN)) {
+      $_node_types[$type_object->type] = $type_object;
+      $_node_names[$type_object->type] = $type_object->name;
+    }
   }
 }
 
 /**
+ * Set default values for a node type defined through hook_node_info().
+ */
+function _node_type_set_defaults($info) {
+  if (!isset($info['has_title'])) {
+    $info['has_title'] = 1;
+  }
+  if ($info['has_title'] && !isset($info['title_label'])) {
+    $info['title_label'] = t('Title');
+  }
+
+  if (!isset($info['has_body'])) {
+    $info['has_body'] = 1;
+  }
+  if ($info['has_body'] && !isset($info['body_label'])) {
+    $info['body_label'] = t('Body');
+  }
+
+  if (!isset($info['status'])) {
+    $info['status'] = NODE_TYPE_NORMAL;
+  }
+  if (($info['status'] & NODE_TYPE_MODIFIABLE_BY_ADMIN) || ($info['status'] & NODE_TYPE_TOGGLEABLE_BY_ADMIN)) {
+    $info['status'] |= NODE_TYPE_VISIBLE_IN_ADMIN_LIST;
+  }
+
+  $info['is_new'] = TRUE;
+
+  return (object) $info;
+}
+
+/**
+ * Return the list of available node types.
+ *
+ * @param $node
+ *   Either a node object, a node array, or a string containing the node type.
+ * @return
+ *   An array consisting of (node type internal name => node type object)
+ *   pairs.
+ */
+function node_get_types() {
+  return _node_types('types');
+}
+
+/**
+ * Return a node type object.
+ *
+ * @param $node
+ *   Either a node object, a node array, or a string containing the node type.
+ * @return
+ *   A node type object.
+ */
+function node_get_type($node) {
+  return _node_types('type', $node);
+}
+
+/**
  * Determine the basename for hook_load etc.
  *
  * @param $node
@@ -238,31 +399,32 @@
  *   The basename for hook_load, hook_nodeapi etc.
  */
 function node_get_base($node) {
-  return _node_names('base', $node);
+  return _node_types('base', $node);
 }
 
 /**
- * Determine the human readable name for a given type.
+ * Return the human-readable names of available node types.
  *
  * @param $node
  *   Either a node object, a node array, or a string containing the node type.
  * @return
- *   The human readable name of the node type.
+ *   An array consisting of (internal node type name => human readable node
+ *   type name) pairs.
  */
-function node_get_name($node) {
-  return _node_names('name', $node);
+function node_get_names() {
+  return _node_types('names');
 }
 
 /**
- * Return the list of available node types.
+ * Determine the human readable name for a given type.
  *
  * @param $node
  *   Either a node object, a node array, or a string containing the node type.
  * @return
- *   An array consisting ('#type' => name) pairs.
+ *   The human readable name of the node type.
  */
-function node_get_types() {
-  return _node_names('list');
+function node_get_name($node, $check_plain = TRUE) {
+  return _node_types('name', $node);
 }
 
 /**
@@ -866,6 +1028,17 @@
       'callback' => 'node_feed',
       'access' => user_access('access content'),
       'type' => MENU_CALLBACK);
+
+    foreach (node_get_types() as $type) {
+      if (($type->status & NODE_TYPE_IS_CONTENT) && ($type->status & NODE_TYPE_ENABLED)) {
+        $name = check_plain($type->name);
+        $items[] = array(
+          'path' => 'node/add/'. $type->type,
+          'title' => t($name),
+          'access' => module_invoke($type->base, 'access', 'create', $type->type),
+        );
+      }
+    }
   }
   else {
     if (arg(0) == 'node' && is_numeric(arg(1))) {
@@ -935,7 +1108,7 @@
                        'moderate-1' => t('in moderation'), 'moderate-0' => t('not in moderation'),
                        'promote-1'  => t('promoted'),      'promote-0' => t('not promoted'),
                        'sticky-1'   => t('sticky'),        'sticky-0' => t('not sticky')));
-  $filters['type'] = array('title' => t('type'), 'options' => node_get_types());
+  $filters['type'] = array('title' => t('type'), 'options' => node_get_names());
   // The taxonomy filter
   if ($taxonomy = module_invoke('taxonomy', 'form_all', 1)) {
     $filters['category'] = array('title' => t('category'), 'options' => $taxonomy);
@@ -1252,7 +1425,7 @@
     $header = array(t('Type'), t('Operations'));
 
     $rows = array();
-    foreach (node_get_types() as $type => $name) {
+    foreach (node_get_names() as $type => $name) {
       $rows[] = array($name, l(t('configure'), 'admin/settings/content-types/'. $type));
     }
 
@@ -1732,21 +1905,23 @@
 function node_add($type) {
   global $user;
 
+  $types = node_get_types();
   // If a node type has been specified, validate its existence.
-  if (array_key_exists($type, node_get_types()) && node_access('create', $type)) {
+  if (isset($types[$type]) && node_access('create', $type)) {
     // Initialize settings:
     $node = array('uid' => $user->uid, 'name' => $user->name, 'type' => $type);
 
     $output = node_form($node);
-    drupal_set_title(t('Submit %name', array('%name' => node_get_name($node))));
+    drupal_set_title(t('Submit %name', array('%name' => check_plain($types[$type]->name))));
   }
   else {
     // If no (valid) node type has been provided, display a node type overview.
-    foreach (node_get_types() as $type => $name) {
-      if (module_invoke(node_get_base($type), 'access', 'create', $type)) {
-        $out = '<dt>'. l($name, "node/add/$type", array('title' => t('Add a new %s.', array('%s' => $name)))) .'</dt>';
-        $out .= '<dd>'. implode("\n", module_invoke_all('help', 'node/add#'. $type)) .'</dd>';
-        $item[$name] = $out;
+    foreach ($types as $type) {
+      if (($type->status & NODE_TYPE_IS_CONTENT) && ($type->status & NODE_TYPE_ENABLED) && module_invoke($type->base, 'access', 'create', $type->type)) {
+        $title = t('Add a new %s.', array('%s' => check_plain($type->name)));
+        $out = '<dt>'. l($type->name, "node/add/$type->type", array('title' => $title)) .'</dt>';
+        $out .= '<dd>'. filter_xss_admin($type->description) .'</dd>';
+        $item[$type->type] = $out;
       }
     }
 
Index: modules/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system.module,v
retrieving revision 1.328
diff -u -r1.328 system.module
--- modules/system.module	5 Jun 2006 08:53:05 -0000	1.328
+++ modules/system.module	18 Jun 2006 14:23:36 -0000
@@ -1017,6 +1017,7 @@
   }
 
   menu_rebuild();
+  node_types_rebuild();
 
   drupal_set_message(t('The configuration options have been saved.'));
   return 'admin/modules';
