=== removed file 'modules/page/page.module'
--- modules/page/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/story.module'
--- modules/story/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/content.module'
--- /dev/null	
+++ modules/content/content.module	
@@ -0,0 +1,555 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Allows administrators to define new content types.
+ */
+
+/**
+ * Implementation of hook_help().
+ */
+function content_help($section) {
+  switch ($section) {
+    case 'admin/modules#description':
+      return t('Allows administrators to define new content types.');
+    case 'admin/node/types':
+      $output = '<p>'. t('Below is a list of all the content types on your site. All posts that exist on your site are instances of one of these content types.') .'</p>';
+      return $output;
+    case 'admin/node/types/add':
+      $output = '<p>'. t('To create a new content type, enter the human-readable name, the machine-readable name, and all other relevant fields that are on this page. Once created, users of your site will be able to create posts that are instances of this content type.');
+      return $output;
+  }
+}
+
+/**
+ * Implementation of hook_perm().
+ */
+function content_perm() {
+  foreach (node_get_types() as $type) {
+    if ($type->module == '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('types'),
+      'callback' => 'content_types_page',
+      'access' => user_access('administer nodes'),
+      'type' => MENU_LOCAL_TASK,
+      'weight' => -5,
+    );
+    $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_name = arg(3);
+      $type_name = !empty($type_name) ? str_replace('-', '_', $type_name) : NULL;
+      $type = node_get_type($type_name);
+
+      if (!empty($type)) {
+        $type->name = check_plain($type->name);
+        $type_url_str = str_replace('_', '-', $type->type);
+
+        $items[] = array(
+          'path' => 'admin/node/types/'. $type_url_str,
+          'title' => t($type->name),
+          'callback' => 'content_type_form',
+          'callback arguments' => array($type, TRUE),
+          'type' => MENU_CALLBACK,
+        );
+        $items[] = array(
+          'path' => 'admin/node/types/'. $type_url_str .'/delete',
+          'title' => t('delete'),
+          'callback' => 'content_type_delete',
+          'callback arguments' => array($type, TRUE),
+          'type' => MENU_CALLBACK,
+        );
+      }
+    }
+  }
+
+  return $items;
+}
+
+/**
+ * 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 ($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) {
+    $form['body_filter']['body'] = array(
+      '#type' => 'textarea',
+      '#title' => check_plain($type->body_label),
+      '#default_value' => $node->body,
+      '#rows' => 20,
+      '#required' => TRUE);
+    $form['body_filter']['format'] = filter_form($node->format);
+  }
+
+  return $form;
+}
+
+/**
+ * Menu callback; display the node type administration page.
+ */
+function content_types_page($type = NULL, $op = NULL) {
+  if (isset($type)) {
+    $type = node_get_type($type);
+    if (isset($type)) {
+      return content_type_form($type);
+    }
+  }
+
+  return content_overview_types();
+}
+
+/**
+ * Displays the content type admin overview page.
+ */
+function content_overview_types() {
+  $types = node_get_types();
+  $names = node_get_names();
+  $header = array(t('Name'), t('Type'), t('Description'), array('data' => t('Operations'), 'colspan' => '2'));
+  $rows = array();
+
+  foreach ($names as $key => $name) {
+    $type = $types[$key];
+    if (module_exist($type->module)) {
+      $name = check_plain($name);
+      $type_url_str = str_replace('_', '-', $type->type);
+      // Populate the operations field.
+      $operations = array();
+
+      // Set the edit column.
+      $operations[] = array('data' => l(t('edit'), 'admin/node/types/'. $type_url_str));
+
+      // Set the delete column.
+      if ($type->custom) {
+        $operations[] = array('data' => l(t('delete'), 'admin/node/types/'. $type_url_str .'/delete'));
+      }
+      else {
+        $operations[] = array('data' => '');
+      }
+
+      $row = array(array('data' => l($name, 'admin/node/types/'. $type_url_str), '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(array('data' => t('No content types available.'), 'colspan' => '5', 'class' => 'message'));
+  }
+
+  return theme('table', $header, $rows);
+}
+
+/**
+ * Generates the node type editing form.
+ */
+function content_type_form($type = NULL, $set_breadcrumb = FALSE) {
+  if (!isset($type->type)) {
+    $type = new stdClass();
+    $type->type = $type->name = $type->module = $type->description = $type->help = '';
+    $type->min_word_count = 0;
+    $type->has_title = TRUE;
+    $type->has_body = TRUE;
+    $type->title_label = t('Title');
+    $type->body_label = t('Body');
+    $type->custom = TRUE;
+    $type->modified = FALSE;
+    $type->locked = FALSE;
+  }
+  else {
+    if ($set_breadcrumb) {
+      _content_type_set_breadcrumb();
+    }
+  }
+
+  $form['identity'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Identification'),
+  );
+  $form['identity']['name'] = array(
+    '#title' => t('Name'),
+    '#type' => 'textfield',
+    '#default_value' => $type->name,
+    '#description' => t('The human-readable name of this content type. This text will be displayed as part of the list on the %create-content page. It is recommended that this name consists only of lowercase letters, numbers, and <strong>spaces</strong>. The name must be unique to this content type.', array('%create-content' => theme('placeholder', t('create content')))),
+    '#required' => TRUE,
+  );
+
+  if (!$type->locked) {
+    $form['identity']['type'] = array(
+      '#title' => t('Type'),
+      '#type' => 'textfield',
+      '#default_value' => $type->type,
+      '#maxlength' => 32,
+      '#required' => TRUE,
+      '#description' => t('The machine-readable name of this content type. This text will be used for constructing the URL of the %create-content page for this content type. It is recommended that this name consists only of lowercase letters, numbers, and <strong>underscores</strong>. Dashes are not allowed. Underscores will be converted into dashes when constructing the URL of the %create-content page. The name must be unique to this content type.', array('%create-content' => theme('placeholder', t('create content')))),
+    );
+  }
+  else {
+    $form['identity']['type'] = array(
+      '#type' => 'value',
+      '#value' => $type->type,
+    );
+    $form['identity']['type_display'] = array(
+      '#title' => t('Type'),
+      '#type' => 'item',
+      '#value' => theme('placeholder', $type->type),
+      '#description' => t('The machine-readable name of this content type. This field cannot be modified for system-defined content types.'),
+    );
+  }
+
+  $form['submission'] = array(
+    '#type' => 'fieldset',
+    '#title' =>t('Submission form'),
+    '#collapsible' => TRUE,
+    '#collapsed' => FALSE,
+  );
+  if ($type->has_title) {
+    $form['submission']['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['submission']['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,
+    );
+  }
+  $form['submission']['description'] = array(
+    '#title' => t('Description'),
+    '#type' => 'textarea',
+    '#default_value' => $type->description,
+    '#description' => t('A brief description of this content type. This text will be displayed as part of the list on the %create-content page.', array('%create-content' => theme('placeholder', t('create content')))),
+  );
+  $form['submission']['help']  = array(
+    '#type' => 'textarea',
+    '#title' => t('Explanation or submission guidelines'),
+    '#default_value' =>  $type->help,
+    '#description' => t('This text will be displayed at the top of the submission form for this content type. It is useful for helping or instructing your users.')
+  );
+  $form['submission']['min_word_count'] = array(
+    '#type' => 'select',
+    '#title' => t('Minimum number of words'),
+    '#default_value' => $type->min_word_count,
+    '#options' => drupal_map_assoc(array(0, 10, 25, 50, 75, 100, 125, 150, 175, 200)),
+    '#description' => t('The minimum number of words for the body field to be considered valid for this content type. This can be useful to rule out submissions that do not meet the site\'s standards, such as short test posts.')
+  );
+  $form['workflow'] = array(
+    '#type' => 'fieldset',
+    '#title' =>t('Workflow'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
+
+  $form['old_type'] = array(
+    '#type' => 'value',
+    '#value' => $type->type,
+  );
+  $form['orig_type'] = array(
+    '#type' => 'value',
+    '#value' => $type->orig_type,
+  );
+  $form['module'] = array(
+    '#type' => 'value',
+    '#value' => $type->module,
+  );
+  $form['custom'] = array(
+    '#type' => 'value',
+    '#value' => $type->custom,
+  );
+  $form['modified'] = array(
+    '#type' => 'value',
+    '#value' => $type->modified,
+  );
+  $form['locked'] = array(
+    '#type' => 'value',
+    '#value' => $type->locked,
+  );
+
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save content type'),
+  );
+
+  if ($type->custom) {
+    if (!empty($type->type)) {
+      $form['delete'] = array(
+        '#type' => 'submit',
+        '#value' => t('Delete content type'),
+      );
+    }
+  }
+  else {
+    $form['reset'] = array(
+      '#type' => 'submit',
+      '#value' => t('Reset to defaults'),
+    );
+  }
+
+  return drupal_get_form('content_type_form', $form);
+}
+
+/**
+ * Implementation of hook_form_validate().
+ */
+function content_type_form_validate($form_id, $form_values) {
+  $type = new stdClass();
+  $type->type = trim($form_values['type']);
+  $type->name = trim($form_values['name']);
+
+  // Work out what the type was before the user submitted this form
+  $old_type = trim($form_values['old_type']);
+  if (empty($old_type)) {
+    $old_type = $type->type;
+  }
+
+  $types = node_get_names();
+
+  if (!$form_values['locked']) {
+    if (isset($types[$type->type]) && $type->type != $old_type) {
+      form_set_error('type', t('The machine-readable name %type is already taken.', array('%type' => theme('placeholder', $type->type))));
+    }
+    if (strpos($type->type, '-') !== FALSE) {
+      form_set_error('type', t('The machine-readable name cannot contain dashes.', array('%type' => theme('placeholder', $type->type))));
+    }
+  }
+
+  $names = array_flip($types);
+
+  if (isset($names[$type->name]) && $names[$type->name] != $old_type) {
+    form_set_error('name', t('The human-readable name %name is already taken.', array('%name' => theme('placeholder', $names[$type->name]))));
+    break;
+  }
+}
+
+/**
+ * Implementation of hook_form_submit().
+ */
+function content_type_form_submit($form_id, $form_values) {
+  $op = isset($_POST['op']) ? $_POST['op'] : '';
+
+  $type = new stdClass();
+
+  $type->type = trim($form_values['type']);
+  $type->name = trim($form_values['name']);
+  $type->orig_type = trim($form_values['orig_type']);
+  $type->old_type = isset($form_values['old_type']) ? $form_values['old_type'] : $type->type;
+
+  $type->description = $form_values['description'];
+  $type->help = $form_values['help'];
+  $type->min_word_count = $form_values['min_word_count'];
+  $type->title_label = $form_values['title_label'];
+  $type->body_label = $form_values['body_label'];
+
+  $type->module = !empty($form_values['module']) ? $form_values['module'] : 'content';
+  $type->has_title = $type->has_body = TRUE;
+  $type->custom = $form_values['custom'];
+  $type->modified = TRUE;
+  $type->locked = $form_values['locked'];
+
+  if ($op == t('Reset to defaults')) {
+    content_type_reset($type);
+  }
+  elseif ($op == t('Delete content type')) {
+    return 'admin/node/types/'. $type->old_type .'/delete';
+  }
+
+  if (!empty($form_values['old_type'])) {
+    if ($type->old_type != $type->type) {
+      $update_count = node_type_update_nodes($type->old_type, $type->type);
+
+      if ($update_count) {
+        $t_args = array('%update_count' => theme('placeholder', $update_count), '%old_type' => theme('placeholder', $type->old_type), '%type' => theme('placeholder', $type->type));
+        drupal_set_message(t('Changed the content type of %update_count posts from %old_type to %type.', $t_args));
+        cache_clear_all('filter:', TRUE);
+      }
+    }
+  }
+
+  $status = node_type_save($type);
+
+  // Remove everything that's been saved already - whatever's left is assumed
+  // to be a persistent variable.
+  foreach ($form_values as $key => $value) {
+    if (isset($type->$key)) {
+      unset($form_values[$key]);
+    }
+  }
+
+  unset($form_values['type_display'], $form_values['old_type'], $form_values['orig_type'], $form_values['submit'], $form_values['delete'], $form_values['reset'], $form_values['form_id']);
+
+  // Save or reset persistent variable values.
+  foreach ($form_values as $key => $value) {
+    $key .= '_'. $type->type;
+    if ($op == t('Reset to defaults')) {
+      variable_del($key);
+    }
+    else {
+      if (is_array($value)) {
+        $value = array_keys(array_filter($value));
+      }
+      variable_set($key, $value);
+
+      if ($type->old_type != $type->type) {
+        $key = str_replace($type->type, $type->old_type, $key);
+        variable_del($key);
+      }
+    }
+  }
+
+  node_types_rebuild();
+  menu_rebuild();
+  cache_clear_all();
+  $t_args = array('%name' => theme('placeholder', $type->name));
+
+  if ($op == t('Reset to defaults')) {
+    drupal_set_message(t('The content type %name has been reset to its default values.', $t_args));
+    return;
+  }
+
+  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'));
+  }
+
+  return 'admin/node/types';
+}
+
+/**
+ * Resets all of the relevant fields of a module-defined node type to their
+ * default values.
+ *
+ * @param &$type
+ *   The node type to reset. The node type is passed back by reference with its
+ *   resetted values. If there is no module-defined info for this node type,
+ *   then nothing happens.
+ */
+function content_type_reset(&$type) {
+  $info_array = module_invoke($type->module, 'node_info');
+  if (isset($info_array[$type->orig_type])) {
+    $info = _node_type_set_defaults($info_array[$type->orig_type]);
+    $info['type'] = $type->orig_type;
+
+    foreach ($info as $field => $value) {
+      $type->$field = $value;
+    }
+  }
+}
+
+/**
+ * Menu callback; delete a single content type.
+ */
+function content_type_delete($type, $set_breadcrumb = FALSE) {
+  if ($set_breadcrumb) {
+    _content_type_set_breadcrumb();
+  }
+
+  $form['type'] = array('#type' => 'value', '#value' => $type->type);
+  $form['name'] = array('#type' => 'value', '#value' => $type->name);
+
+  $message = t('Are you sure you want to delete the content type %type?', array('%type' => theme('placeholder', $type->name)));
+
+  return confirm_form('content_type_delete_form', $form, $message, 'admin/node/types', t('This action cannot be undone.'), t('Delete'));
+}
+
+/**
+ * Process content type delete form submissions.
+ */
+function content_type_delete_form_submit($form_id, $form_values) {
+  node_type_delete($form_values['type']);
+
+  $t_args = array('%name' => theme('placeholder', $form_values['name']));
+  drupal_set_message(t('The content type %name has been deleted.', $t_args));
+  watchdog('menu', t('Deleted content type %name.', $t_args), WATCHDOG_NOTICE);
+
+  node_types_rebuild();
+  menu_rebuild();
+
+  return 'admin/node/types';
+}
+
+/**
+ * Fixes up the breadcrumb for 'edit' and 'delete' pages to include a link to
+ * the 'admin/node/types' page.
+ */
+function _content_type_set_breadcrumb() {
+  $breadcrumb = drupal_get_breadcrumb();
+  if (count($breadcrumb) > 3) {
+    array_pop($breadcrumb);
+  }
+  $breadcrumb[] = l(t('types'), 'admin/node/types');
+  drupal_set_breadcrumb($breadcrumb);
+}
+
Index: database/database.4.0.mysql
===================================================================
RCS file: /cvs/drupal/drupal/database/database.4.0.mysql,v
retrieving revision 1.7
diff -u -r1.7 database.4.0.mysql
--- database/database.4.0.mysql	12 Jul 2006 14:38:38 -0000	1.7
+++ database/database.4.0.mysql	12 Jul 2006 17:30:35 -0000
@@ -460,6 +460,28 @@
 );
 
 --
+-- Table structure for table 'node_type'
+--
+
+CREATE TABLE node_type (
+  type varchar(32) NOT NULL,
+  name varchar(255) NOT NULL default '',
+  module 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 '',
+  min_word_count smallint(4) unsigned NOT NULL,
+  custom tinyint(1) NOT NULL DEFAULT '0',
+  modified tinyint(1) NOT NULL DEFAULT '0',
+  locked tinyint(1) NOT NULL DEFAULT '0',
+  orig_type varchar(255) NOT NULL default '',
+  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.7
diff -u -r1.7 database.4.1.mysql
--- database/database.4.1.mysql	12 Jul 2006 14:38:38 -0000	1.7
+++ database/database.4.1.mysql	12 Jul 2006 17:30:36 -0000
@@ -491,6 +491,29 @@
 DEFAULT CHARACTER SET utf8;
 
 --
+-- Table structure for table 'node_type'
+--
+
+CREATE TABLE node_type (
+  type varchar(32) NOT NULL default '',
+  name varchar(255) NOT NULL,
+  module varchar(255) 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 '',
+  description mediumtext NOT NULL,
+  help mediumtext NOT NULL,
+  min_word_count smallint(4) unsigned NOT NULL,
+  custom tinyint(1) NOT NULL DEFAULT '0',
+  modified tinyint(1) NOT NULL DEFAULT '0',
+  locked tinyint(1) NOT NULL DEFAULT '0',
+  orig_type varchar(255) NOT NULL default '',
+  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.179
diff -u -r1.179 database.pgsql
--- database/database.pgsql	12 Jul 2006 14:38:38 -0000	1.179
+++ database/database.pgsql	12 Jul 2006 17:30:37 -0000
@@ -468,6 +468,28 @@
 CREATE SEQUENCE node_revisions_vid_seq INCREMENT 1 START 1;
 
 --
+-- Table structure for table 'node_type'
+--
+
+CREATE TABLE node_type (
+  type varchar(32) NOT NULL default '',
+  name varchar(255) NOT NULL,
+  module 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 '',
+  min_word_count integer unsigned NOT NULL,
+  custom smallint NOT NULL DEFAULT '0',
+  modified smallint NOT NULL DEFAULT '0',
+  locked smallint NOT NULL DEFAULT '0',
+  orig_type varchar(255) NOT NULL default '',
+  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.240
diff -u -r1.240 updates.inc
--- database/updates.inc	5 Jul 2006 11:45:50 -0000	1.240
+++ database/updates.inc	12 Jul 2006 17:30:39 -0000
@@ -2102,3 +2102,110 @@
   }
   return $ret;
 }
+
+function system_update_188() {
+  // 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(32) NOT NULL,
+        name varchar(255) NOT NULL,
+        module 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 '',
+        min_word_count smallint(4) unsigned NOT NULL,
+        custom tinyint(1) NOT NULL DEFAULT '0',
+        modified tinyint(1) NOT NULL DEFAULT '0',
+        locked tinyint(1) NOT NULL DEFAULT '0',
+        orig_type varchar(255) NOT NULL default '',
+        PRIMARY KEY (type)
+        ) /*!40100 DEFAULT CHARACTER SET utf8 */;");
+      break;
+
+    case 'pgsql':
+      $ret[] = update_sql("CREATE TABLE {node_type} (
+        type varchar(32) NOT NULL,
+        name varchar(255) NOT NULL,
+        module 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 '',
+        min_word_count integer unsigned NOT NULL,
+        custom smallint NOT NULL DEFAULT '0',
+        modified smallint NOT NULL DEFAULT '0',
+        locked smallint NOT NULL DEFAULT '0',
+        orig_type varchar(255) NOT NULL default '',
+        PRIMARY KEY (type)
+        );");
+      break;
+  }
+
+  // Insert default user-defined node types into the database.
+  $types = array(
+    array(
+      'type' => 'page',
+      'name' => t('page'),
+      'module' => '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,
+      'locked' => FALSE,
+    ),
+    array(
+      'type' => 'story',
+      'name' => t('story'),
+      'module' => '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.'),
+      'custom' => TRUE,
+      'modified' => TRUE,
+      'locked' => FALSE,
+    )
+  );
+
+  foreach ($types as $type) {
+    $type = (object) _node_type_set_defaults($type);
+    node_type_save($type);
+  }
+
+  $filename = 'modules/content.module';
+  $is_existing = db_num_rows(db_query("SELECT * FROM {system} WHERE filename = '%s'", $filename));
+
+  // Enable the new content module.
+  if ($is_existing) {
+    db_query("UPDATE {system} SET status = %d WHERE filename = '%s'", 1, $filename);
+  }
+  else {
+    db_query("INSERT INTO {system} (name, description, type, filename, status, throttle, bootstrap, schema_version) VALUES ('%s', '%s', '%s', '%s', %d, %d, %d, %d)", 'content', '', 'module', $filename, 1, 0, 0, 0);
+  }
+
+  cache_clear_all();
+  system_modules();
+  menu_rebuild();
+  node_types_rebuild();
+
+  // Migrate old values for 'minimum_x_size' variables to the node_type table.
+  $query = db_query('SELECT type FROM {node_type}');
+  while ($result = db_fetch_object($query)) {
+    $variable_name = 'minimum_'. $result->type .'_size';
+    if ($value = db_fetch_object(db_query("SELECT value FROM {variable} WHERE name = '%s'", $variable_name))) {
+      $value = (int) unserialize($value->value);
+      db_query("UPDATE {node_type} SET min_word_count = %d, modified = %d WHERE type = '%s'", $value, 1, $result->type);
+      variable_del($variable_name);
+    }
+  }
+
+  node_types_rebuild();
+
+  return $ret;
+}
Index: misc/drupal.css
===================================================================
RCS file: /cvs/drupal/drupal/misc/drupal.css,v
retrieving revision 1.151
diff -u -r1.151 drupal.css
--- misc/drupal.css	30 Jun 2006 00:13:32 -0000	1.151
+++ misc/drupal.css	12 Jul 2006 17:30:39 -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/blog/blog.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/blog/blog.module,v
retrieving revision 1.250
diff -u -r1.250 blog.module
--- modules/blog/blog.module	4 Jul 2006 08:59:04 -0000	1.250
+++ modules/blog/blog.module	12 Jul 2006 17:30:40 -0000
@@ -10,7 +10,13 @@
  * Implementation of hook_node_info().
  */
 function blog_node_info() {
-  return array('blog' => array('name' => t('blog entry'), 'base' => 'blog'));
+  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. A blog is tightly coupled to the author so each user will have his 'own' blog."),
+    )
+  );
 }
 
 /**
@@ -72,8 +78,6 @@
       return $output;
     case 'admin/modules#description':
       return t('Enables keeping an easily and regularly updated web page or a blog.');
-    case 'node/add#blog':
-      return t("A blog is a regularly updated journal or diary made up of individual posts shown in reversed chronological order. A blog is tightly coupled to the author so each user will have his 'own' blog.");
   }
 }
 
@@ -201,6 +205,7 @@
 function blog_form(&$node) {
   global $nid;
   $iid = $_GET['iid'];
+  $type = node_get_type($node);
 
 
   if (empty($node->body)) {
@@ -221,8 +226,8 @@
 
   }
 
-  $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['title'] = array('#type' => 'textfield', '#title' => check_plain($type->title_label), '#required' => TRUE, '#default_value' => $node->title, '#weight' => -5);
+  $form['body_filter']['body'] = array('#type' => 'textarea', '#title' => check_plain($type->body_label), '#default_value' => $node->body, '#rows' => 20, '#required' => TRUE);
   $form['body_filter']['filter'] = filter_form($node->format);
   return $form;
 }
Index: modules/book/book.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/book/book.module,v
retrieving revision 1.374
diff -u -r1.374 book.module
--- modules/book/book.module	10 Jul 2006 08:05:15 -0000	1.374
+++ modules/book/book.module	12 Jul 2006 17:30:41 -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'),
+      '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."),
+    )
+  );
 }
 
 /**
@@ -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',
@@ -225,6 +227,7 @@
  * Implementation of hook_form().
  */
 function book_form(&$node) {
+  $type = node_get_type($node);
   if ($node->nid && !$node->parent && !user_access('create new books')) {
     $form['parent'] = array('#type' => 'value', '#value' => $node->parent);
   }
@@ -239,13 +242,13 @@
   }
 
   $form['title'] = array('#type' => 'textfield',
-    '#title' => t('Title'),
+    '#title' => check_plain($type->title_label),
     '#required' => TRUE,
     '#default_value' => $node->title,
     '#weight' => -5,
   );
   $form['body_filter']['body'] = array('#type' => 'textarea',
-    '#title' => t('Body'),
+    '#title' => check_plain($type->body_label),
     '#default_value' => $node->body,
     '#rows' => 20,
     '#required' => TRUE,
@@ -1014,8 +1017,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/comment/comment.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v
retrieving revision 1.464
diff -u -r1.464 comment.module
--- modules/comment/comment.module	10 Jul 2006 19:27:52 -0000	1.464
+++ modules/comment/comment.module	12 Jul 2006 17:30:43 -0000
@@ -259,10 +259,10 @@
 }
 
 function comment_form_alter($form_id, &$form) {
-  if (isset($form['type'])) {
-    if ($form['type']['#value'] .'_node_settings' == $form_id) {
-      $form['workflow']['comment_'. $form['type']['#value']] = array('#type' => 'radios', '#title' => t('Default comment setting'), '#default_value' => variable_get('comment_'. $form['type']['#value'], COMMENT_NODE_READ_WRITE), '#options' => array(t('Disabled'), t('Read only'), t('Read/Write')), '#description' => t('Users with the <em>administer comments</em> permission will be able to override this setting.'));
-    }
+  if (isset($form['identity']['type']) && $form_id == 'content_type_form') {
+    $form['workflow']['comment'] = array('#type' => 'radios', '#title' => t('Default comment setting'), '#default_value' => variable_get('comment_'. $form['identity']['type']['#default_value'], COMMENT_NODE_READ_WRITE), '#options' => array(t('Disabled'), t('Read only'), t('Read/Write')), '#description' => t('Users with the <em>administer comments</em> permission will be able to override this setting.'));
+  }
+  elseif (isset($form['type'])) {
     if ($form['type']['#value'] .'_node_form' == $form_id) {
       $node = $form['#node'];
       if (user_access('administer comments')) {
Index: modules/forum/forum.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/forum/forum.module,v
retrieving revision 1.338
diff -u -r1.338 forum.module
--- modules/forum/forum.module	11 Jul 2006 07:04:34 -0000	1.338
+++ modules/forum/forum.module	12 Jul 2006 17:30:44 -0000
@@ -33,8 +33,6 @@
       return t('<p>Containers help you organize your forums. The job of a container is to hold, or contain, other forums that are related. For example, a container named "Food" might hold two forums named "Fruit" and "Vegetables".</p>');
     case 'admin/forum/add/forum':
       return t('<p>A forum holds discussion topics that are related. For example, a forum named "Fruit" might contain topics titled "Apples" and "Bananas".</p>');
-    case 'node/add#forum':
-      return t('Create a new topic for discussion in the forums.');
   }
 }
 
@@ -105,7 +103,14 @@
  * Implementation of hook_node_info().
  */
 function forum_node_info() {
-  return array('forum' => array('name' => t('forum topic'), 'base' => 'forum'));
+  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'),
+    )
+  );
 }
 
 /**
@@ -377,7 +382,8 @@
  * Implementation of hook_form().
  */
 function forum_form(&$node) {
-  $form['title'] = array('#type' => 'textfield', '#title' => t('Subject'), '#default_value' => $node->title, '#required' => TRUE, '#weight' => -5);
+  $type = node_get_type($node);
+  $form['title'] = array('#type' => 'textfield', '#title' => check_plain($type->title_label), '#default_value' => $node->title, '#required' => TRUE, '#weight' => -5);
 
   if ($node->nid) {
     $forum_terms = taxonomy_node_get_terms_by_vocabulary(_forum_get_vid(), $node->nid);
@@ -386,7 +392,7 @@
     $form['shadow'] = array('#type' => 'checkbox', '#title' => t('Leave shadow copy'), '#default_value' => $shadow, '#description' => t('If you move this topic, you can leave a link in the old forum to the new forum.'));
   }
 
-  $form['body_filter']['body'] = array('#type' => 'textarea', '#title' => t('Body'), '#default_value' => $node->body, '#rows' => 20, '#required' => TRUE);
+  $form['body_filter']['body'] = array('#type' => 'textarea', '#title' => check_plain($type->body_label), '#default_value' => $node->body, '#rows' => 20, '#required' => TRUE);
   $form['body_filter']['format'] = filter_form($node->format);
 
   return $form;
Index: modules/menu/menu.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/menu/menu.module,v
retrieving revision 1.75
diff -u -r1.75 menu.module
--- modules/menu/menu.module	22 Jun 2006 09:50:57 -0000	1.75
+++ modules/menu/menu.module	12 Jul 2006 17:30:45 -0000
@@ -682,7 +682,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/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.656
diff -u -r1.656 node.module
--- modules/node/node.module	10 Jul 2006 19:27:52 -0000	1.656
+++ modules/node/node.module	12 Jul 2006 17:30:47 -0000
@@ -45,7 +45,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);
   }
 }
 
@@ -192,16 +193,32 @@
   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'];
-    }
+/**
+ * Builds a list of available node types, and returns all of part of this list
+ * in the specified format.
+ *
+ * @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.
+ * @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.
+ * @param $reset
+ *   Whether or not to reset this function's internal cache (defaults to
+ *   FALSE).
+ *
+ * @return
+ *   Either an array of all available node types, or a single node type, in a
+ *   variable format.
+ */
+function _node_types($op = '', $node = NULL, $reset = FALSE) {
+  global $_node_types, $_node_names;
+
+  if ($reset || !isset($_node_types)) {
+    _node_types_build();
   }
+
   if ($node) {
     if (is_array($node)) {
       $type = $node['type'];
@@ -212,54 +229,222 @@
     elseif (is_string($node)) {
       $type = $node;
     }
-    if (!isset($node_names[$type])) {
+    if (!isset($_node_types[$type])) {
       return FALSE;
     }
   }
   switch ($op) {
-    case 'base':
-      return $node_names[$type]['base'];
-    case 'list':
-      return $node_list;
+    case 'types':
+      return $_node_types;
+    case 'type':
+      return $_node_types[$type];
+    case 'module':
+      return $_node_types[$type]->module;
+    case 'names':
+      return $_node_names;
     case 'name':
-      return $node_list[$type];
+      return $_node_names[$type];
+  }
+}
+
+/**
+ * Resets the database cache of node types, and saves all new or non-modified
+ * module-defined node types to the database.
+ */
+function node_types_rebuild() {
+  _node_types_build();
+
+  $node_types = node_get_types();
+
+  foreach ($node_types as $type => $info) {
+    if (!empty($info->is_new)) {
+      node_type_save($info);
+    }
+  }
+
+  _node_types_build();
+}
+
+/**
+ * Saves a node type to the database.
+ *
+ * @param $info
+ *   The node type to save, as an object.
+ *
+ * @return
+ *   Status flag indicating outcome of the operation.
+ */
+function node_type_save($info) {
+  $is_existing = FALSE;
+  $existing_type = !empty($info->old_type) ? $info->old_type : $info->type;
+  $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);
+    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);
+    return SAVED_NEW;
+  }
+}
+
+/**
+ * Deletes a node type from the database.
+ *
+ * @param $type
+ *   The node type to delete, as a string.
+ */
+function node_type_delete($type) {
+  db_query("DELETE FROM {node_type} WHERE type = '%s'", $type);
+}
+
+/**
+ * Updates all nodes of one type to be of another type.
+ *
+ * @param $orig_type
+ *   The current node type of the nodes.
+ * @param $type
+ *   The new node type of the nodes.
+ *
+ * @return
+ *   The number of nodes whose node type field was modified.
+ */
+function node_type_update_nodes($old_type, $type) {
+  return db_num_rows(db_query("UPDATE {node} SET type = '%s' WHERE type = '%s'", $type, $old_type));
+}
+
+/**
+ * Builds the list of available node types, by querying hook_node_info() in all
+ * modules, and by looking for node types in the database.
+ *
+ */
+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] = (object) _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->modified) {
+      $_node_types[$type_object->type] = $type_object;
+      $_node_names[$type_object->type] = $type_object->name;
+
+      if ($type_object->type != $type_object->orig_type) {
+        unset($_node_types[$type_object->orig_type]);
+        unset($_node_names[$type_object->orig_type]);
+      }
+    }
+  }
+
+  asort($_node_names);
+}
+
+/**
+ * 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['custom'])) {
+    $info['custom'] = FALSE;
+  }
+  if (!isset($info['modified'])) {
+    $info['modified'] = FALSE;
+  }
+  if (!isset($info['locked'])) {
+    $info['locked'] = TRUE;
+  }
+
+  $info['orig_type'] = $info['type'];
+  $info['is_new'] = TRUE;
+
+  return $info;
 }
 
 /**
- * Determine the basename for hook_load etc.
+ * Return the list of available node types.
  *
  * @param $node
  *   Either a node object, a node array, or a string containing the node type.
  * @return
- *   The basename for hook_load, hook_nodeapi etc.
+ *   An array consisting of (node type internal name => node type object)
+ *   pairs.
  */
-function node_get_base($node) {
-  return _node_names('base', $node);
+function node_get_types() {
+  return _node_types('types');
 }
 
 /**
- * Determine the human readable name for a given type.
+ * Return a node type object.
  *
  * @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.
+ *   A node type object.
  */
-function node_get_name($node) {
-  return _node_names('name', $node);
+function node_get_type($node) {
+  return _node_types('type', $node);
 }
 
 /**
- * Return the list of available node types.
+ * Determine the module name for hook_load etc.
  *
  * @param $node
  *   Either a node object, a node array, or a string containing the node type.
  * @return
- *   An array consisting ('#type' => name) pairs.
+ *   The module name for hook_load, hook_nodeapi etc.
  */
-function node_get_types() {
-  return _node_names('list');
+function node_get_module($node) {
+  return _node_types('module', $node);
+}
+
+/**
+ * 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
+ *   An array consisting of (internal node type name => human readable node
+ *   type name) pairs.
+ */
+function node_get_names() {
+  return _node_types('names');
+}
+
+/**
+ * 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
+ *   The human readable name of the node type.
+ */
+function node_get_name($node, $check_plain = TRUE) {
+  return _node_types('name', $node);
 }
 
 /**
@@ -273,7 +458,7 @@
  *   TRUE iff the $hook exists in the node type of $node.
  */
 function node_hook(&$node, $hook) {
-  return module_hook(node_get_base($node), $hook);
+  return module_hook(node_get_module($node), $hook);
 }
 
 /**
@@ -290,7 +475,7 @@
  */
 function node_invoke(&$node, $hook, $a2 = NULL, $a3 = NULL, $a4 = NULL) {
   if (node_hook($node, $hook)) {
-    $function = node_get_base($node) ."_$hook";
+    $function = node_get_module($node) ."_$hook";
     return ($function($node, $a2, $a3, $a4));
   }
 }
@@ -582,7 +767,7 @@
  * Implementation of hook_perm().
  */
 function node_perm() {
-  return array('administer nodes', 'access content', 'view revisions', 'revert revisions');
+  return array('administer content types', 'administer nodes', 'access content', 'view revisions', 'revert revisions');
 }
 
 /**
@@ -844,9 +1029,6 @@
     $items[] = array('path' => 'admin/settings/node', 'title' => t('posts'),
       'callback' => 'node_configure',
       'access' => user_access('administer nodes'));
-    $items[] = array('path' => 'admin/settings/content-types', 'title' => t('content types'),
-      'callback' => 'node_types_configure',
-      'access' => user_access('administer nodes'));
 
     $items[] = array('path' => 'node', 'title' => t('content'),
       'callback' => 'node_page',
@@ -861,6 +1043,18 @@
       'callback' => 'node_feed',
       'access' => user_access('access content'),
       'type' => MENU_CALLBACK);
+
+    foreach (node_get_types() as $type) {
+      if (module_exist($type->module)) {
+        $name = check_plain($type->name);
+        $type_url_str = str_replace('_', '-', $type->type);
+        $items[] = array(
+          'path' => 'node/add/'. $type_url_str,
+          'title' => t($name),
+          'access' => module_invoke($type->module, 'access', 'create', $type->type),
+        );
+      }
+    }
   }
   else {
     if (arg(0) == 'node' && is_numeric(arg(1))) {
@@ -890,11 +1084,6 @@
           'type' => MENU_LOCAL_TASK);
       }
     }
-    else if (arg(0) == 'admin' && arg(1) == 'settings' && arg(2) == 'content-types' && is_string(arg(3))) {
-      $items[] = array('path' => 'admin/settings/content-types/'. arg(3),
-        'title' => t("'%name' content type", array('%name' => node_get_name(arg(3)))),
-        'type' => MENU_CALLBACK);
-    }
   }
 
   return $items;
@@ -929,7 +1118,7 @@
     'options' => array('status-1'   => t('published'),     'status-0' => t('not published'),
                        '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);
@@ -1221,40 +1410,6 @@
 }
 
 /**
- * Menu callback; presents each node type configuration page.
- */
-function node_types_configure($type = NULL) {
-  if (isset($type)) {
-    $node = new stdClass();
-    $node->type = $type;
-    $form['submission'] = array('#type' => 'fieldset', '#title' =>t('Submission form') );
-    $form['submission'][$type . '_help']  = array(
-      '#type' => 'textarea', '#title' => t('Explanation or submission guidelines'), '#default_value' =>  variable_get($type .'_help', ''),
-      '#description' => t('This text will be displayed at the top of the %type submission form. It is useful for helping or instructing your users.', array('%type' => node_get_name($type)))
-    );
-    $form['submission']['minimum_'. $type .'_size'] = array(
-      '#type' => 'select', '#title' => t('Minimum number of words'), '#default_value' => variable_get('minimum_'. $type .'_size', 0), '#options' => drupal_map_assoc(array(0, 10, 25, 50, 75, 100, 125, 150, 175, 200)),
-      '#description' => t('The minimum number of words a %type must be to be considered valid. This can be useful to rule out submissions that do not meet the site\'s standards, such as short test posts.', array('%type' => node_get_name($type)))
-    );
-    $form['workflow'] = array('#type' => 'fieldset', '#title' =>t('Workflow'));
-    $form['type'] = array('#type' => 'value', '#value' => $type);
-
-    $form['array_filter'] = array('#type' => 'value', '#value' => TRUE);
-    return system_settings_form($type .'_node_settings', $form);
-  }
-  else {
-    $header = array(t('Type'), t('Operations'));
-
-    $rows = array();
-    foreach (node_get_types() as $type => $name) {
-      $rows[] = array($name, l(t('configure'), 'admin/settings/content-types/'. $type));
-    }
-
-    return theme('table', $header, $rows);
-  }
-}
-
-/**
  * Generate an overview table of older revisions of a node.
  */
 function node_revision_overview($node) {
@@ -1725,21 +1880,25 @@
 function node_add($type) {
   global $user;
 
+  $types = node_get_types();
+  $type = isset($type) ? str_replace('-', '_', $type) : NULL;
   // 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 (module_exist($type->module) && module_invoke($type->module, 'access', 'create', $type->type)) {
+        $type_url_str = str_replace('_', '-', $type->type);
+        $title = t('Add a new %s.', array('%s' => check_plain($type->name)));
+        $out = '<dt>'. l($type->name, "node/add/$type_url_str", array('title' => $title)) .'</dt>';
+        $out .= '<dd>'. filter_xss_admin($type->description) .'</dd>';
+        $item[$type->type] = $out;
       }
     }
 
@@ -1748,7 +1907,7 @@
       $output = t('Choose the appropriate item from the list:') .'<dl>'. implode('', $item) .'</dl>';
     }
     else {
-      $output = t('You are not allowed to create content.');
+      $output = t('No content types available.');
     }
   }
 
@@ -2132,10 +2291,10 @@
  */
 function node_form_alter($form_id, &$form) {
   // Node publishing options
-  if (isset($form['type']) && $form['type']['#value'] .'_node_settings' == $form_id) {
-    $form['workflow']['node_options_'. $form['type']['#value']] = array('#type' => 'checkboxes',
+  if (isset($form['identity']['type']) && $form_id == 'content_type_form') {
+    $form['workflow']['node_options'] = array('#type' => 'checkboxes',
       '#title' => t('Default options'),
-      '#default_value' => variable_get('node_options_'. $form['type']['#value'], array('status', 'promote')),
+      '#default_value' => variable_get('node_options_'. $form['identity']['type']['#default_value'], array('status', 'promote')),
       '#options' => array(
         'status' => t('Published'),
         'promote' => t('Promoted to front page'),
@@ -2320,7 +2479,7 @@
 
   // Can't use node_invoke(), because the access hook takes the $op parameter
   // before the $node parameter.
-  $access = module_invoke(node_get_base($node), 'access', $op, $node);
+  $access = module_invoke(node_get_module($node), '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.202
diff -u -r1.202 poll.module
--- modules/poll/poll.module	10 Jul 2006 08:05:15 -0000	1.202
+++ modules/poll/poll.module	12 Jul 2006 17:30:48 -0000
@@ -25,8 +25,6 @@
       return $output;
     case 'admin/modules#description':
       return t("Allows your site to capture votes on different topics in the form of multiple choice questions.");
-    case 'node/add#poll':
-      return t("A poll is a multiple-choice question which visitors can vote on.");
   }
 }
 
@@ -128,8 +126,9 @@
  */
 function poll_form(&$node) {
   $admin = user_access('administer nodes');
+  $type = node_get_type($node);
 
-  $form['title'] = array('#type' => 'textfield', '#title' => t('Question'), '#required' => TRUE, '#default_value' => $node->title, '#weight' => -1);
+  $form['title'] = array('#type' => 'textfield', '#title' => check_plain($type->title_label), '#required' => TRUE, '#default_value' => $node->title, '#weight' => -1);
 
   $form['choice']['choices'] = array('#type' => 'hidden', '#default_value' => max(2, count($node->choice) ? count($node->choice) : 5));
   $form['choice']['morechoices'] = array('#type' => 'checkbox', '#title' => t('Need more choices'), '#default_value' => 0, '#description' => t("If the amount of boxes above isn't enough, check this box and click the Preview button below to add some more."), '#weight' => 1);
@@ -271,7 +270,15 @@
  * Implementation of hook_node_info().
  */
 function poll_node_info() {
-  return array('poll' => array('name' => t("poll"), 'base' => 'poll'));
+  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,
+    )
+  );
 }
 
 function poll_page() {
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.332
diff -u -r1.332 system.module
--- modules/system/system.module	10 Jul 2006 21:12:09 -0000	1.332
+++ modules/system/system.module	12 Jul 2006 17:30:50 -0000
@@ -1113,6 +1113,7 @@
   }
 
   menu_rebuild();
+  node_types_rebuild();
 
   drupal_set_message(t('The configuration options have been saved.'));
   return 'admin/modules';
@@ -1241,7 +1242,7 @@
     );
 
     // Toggle node display.
-    $node_types = module_invoke('node', 'get_types');
+    $node_types = node_get_names();
     if ($node_types) {
       $form['node_info'] = array(
         '#type' => 'fieldset',
Index: modules/upload/upload.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/upload/upload.module,v
retrieving revision 1.112
diff -u -r1.112 upload.module
--- modules/upload/upload.module	10 Jul 2006 19:27:52 -0000	1.112
+++ modules/upload/upload.module	12 Jul 2006 17:30:51 -0000
@@ -291,14 +291,14 @@
 }
 
 function upload_form_alter($form_id, &$form) {
-  if (isset($form['type'])) {
-    if ($form['type']['#value'] .'_node_settings' == $form_id) {
-      $form['workflow']['upload_'. $form['type']['#value']] = array(
-        '#type' => 'radios', '#title' => t('Attachments'), '#default_value' => variable_get('upload_'. $form['type']['#value'], 1),
-        '#options' => array(t('Disabled'), t('Enabled')),
-      );
-    }
+  if (isset($form['identity']['type']) && $form_id == 'content_type_form') {
+    $form['workflow']['upload'] = array(
+      '#type' => 'radios', '#title' => t('Attachments'), '#default_value' => variable_get('upload_'. $form['identity']['type']['#default_value'], 1),
+      '#options' => array(t('Disabled'), t('Enabled')),
+    );
+  }
 
+  if (isset($form['type'])) {
     $node = $form['#node'];
     if ($form['type']['#value'] .'_node_form' == $form_id && variable_get("upload_$node->type", TRUE) && user_access('upload files')) {
       drupal_add_js('misc/progress.js');
