=== modified file 'includes/common.inc'
--- includes/common.inc	2010-10-04 17:46:00 +0000
+++ includes/common.inc	2010-10-05 22:44:33 +0000
@@ -6595,8 +6595,10 @@ function drupal_flush_all_caches() {
   system_rebuild_theme_data();
   drupal_theme_rebuild();
 
-  menu_rebuild();
   node_types_rebuild();
+  // node_menu defines menu items based on node types so it needs to come
+  // after node types are rebuilt.
+  menu_rebuild();
 
   // Synchronize to catch any actions that were added or removed.
   actions_synchronize();

=== modified file 'modules/forum/forum.install'
--- modules/forum/forum.install	2010-10-01 01:37:13 +0000
+++ modules/forum/forum.install	2010-10-05 22:31:20 +0000
@@ -71,30 +71,30 @@ function forum_enable() {
     );
     $term = (object) $edit;
     taxonomy_term_save($term);
-  }
 
-  // Create the instance on the bundle.
-  $instance = array(
-    'field_name' => 'taxonomy_' . $vocabulary->machine_name,
-    'entity_type' => 'node',
-    'label' => $vocabulary->name,
-    'bundle' => 'forum',
-    'required' => TRUE,
-    'widget' => array(
-      'type' => 'options_select',
-    ),
-    'display' => array(
-      'default' => array(
-        'type' => 'taxonomy_term_reference_link',
-        'weight' => 10,
-      ),
-      'teaser' => array(
-        'type' => 'taxonomy_term_reference_link',
-        'weight' => 10,
+    // Create the instance on the bundle.
+    $instance = array(
+      'field_name' => 'taxonomy_' . $vocabulary->machine_name,
+      'entity_type' => 'node',
+      'label' => $vocabulary->name,
+      'bundle' => 'forum',
+      'required' => TRUE,
+      'widget' => array(
+        'type' => 'options_select',
+      ),
+      'display' => array(
+        'default' => array(
+          'type' => 'taxonomy_term_reference_link',
+         'weight' => 10,
+        ),
+        'teaser' => array(
+          'type' => 'taxonomy_term_reference_link',
+         'weight' => 10,
+        ),
       ),
-    ),
-  );
-  field_create_instance($instance);
+    );
+    field_create_instance($instance);
+  }
 
   // Ensure the forum node type is available.
   node_types_rebuild();

=== modified file 'modules/node/content_types.inc'
--- modules/node/content_types.inc	2010-09-03 17:47:20 +0000
+++ modules/node/content_types.inc	2010-10-05 22:31:20 +0000
@@ -320,6 +320,9 @@ function node_type_form_submit($form, &$
   $type->custom = $form_state['values']['custom'];
   $type->modified = TRUE;
   $type->locked = $form_state['values']['locked'];
+  if (isset($form['#node_type']->defining_module)) {
+    $type->defining_module = $form['#node_type']->defining_module;
+  }
 
   if ($op == t('Delete content type')) {
     $form_state['redirect'] = 'admin/structure/types/manage/' . str_replace('_', '-', $type->old_type) . '/delete';

=== modified file 'modules/node/node.install'
--- modules/node/node.install	2010-10-01 22:03:29 +0000
+++ modules/node/node.install	2010-10-05 23:25:12 +0000
@@ -294,6 +294,12 @@ function node_schema() {
         'length' => 255,
         'not null' => TRUE,
       ),
+      'defining_module' => array(
+        'description' => 'The module defining this node type.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+      ),
       'description' => array(
         'description' => 'A brief description of this type.',
         'type' => 'text',
@@ -344,6 +350,13 @@ function node_schema() {
         'default' => 0,
         'size' => 'tiny',
       ),
+      'disabled' => array(
+        'description' => 'A boolean indicating whether the node type is disabled.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'size' => 'tiny'
+      ),
       'orig_type' => array(
         'description' => 'The original machine-readable name of this node type. This may be different from the current type name if the locked field is 0.',
         'type' => 'varchar',
@@ -438,13 +451,36 @@ function _update_7000_node_get_types() {
  */
 
 /**
- * Fix node type 'module' attribute to avoid name-space conflicts.
+ * Upgrade the node type table and fix node type 'module' attribute to avoid name-space conflicts.
  */
 function node_update_7000() {
+  db_add_field('node_type', 'defining_module', array(
+    'description' => 'The module defining this node type.',
+    'type' => 'varchar',
+    'default' => '',
+    'length' => 255,
+    'not null' => TRUE,
+  ));
+
+  db_add_field('node_type', 'disabled', array(
+    'description' => 'A boolean indicating whether the node type is disabled.',
+    'type' => 'int',
+    'not null' => TRUE,
+    'default' => 0,
+    'size' => 'tiny'
+  ));
+
   db_update('node_type')
     ->fields(array('module' => 'node_content'))
     ->condition('module', 'node')
     ->execute();
+  $modules = db_select('system', 's')
+    ->fields('s', array('name'))
+    ->condition('type', 'module')
+    ->fetchCol();
+  db_update('node_type')
+    ->expression('defining_module', 'module')
+    ->condition('module', $modules);
 
   // Rename the module column to base.
   db_change_field('node_type', 'module', 'base', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE));

=== modified file 'modules/node/node.module'
--- modules/node/node.module	2010-10-03 01:15:33 +0000
+++ modules/node/node.module	2010-10-05 23:18:44 +0000
@@ -461,16 +461,7 @@ function node_type_get_name($node) {
  * and obsolete types.
  */
 function node_types_rebuild() {
-  // Reset and load updated node types.
-  drupal_static_reset('_node_types_build');
-  foreach (node_type_get_types() as $type => $info) {
-    if (!empty($info->is_new)) {
-      node_type_save($info);
-    }
-    if (!empty($info->disabled)) {
-      node_type_delete($info->type);
-    }
-  }
+  _node_types_build(TRUE);
 }
 
 /**
@@ -513,6 +504,8 @@ function node_type_save($info) {
     'custom' => (int) $type->custom,
     'modified' => (int) $type->modified,
     'locked' => (int) $type->locked,
+    'disabled' => (int) $type->disabled,
+    'defining_module' => $type->defining_module,
   );
 
   if ($is_existing) {
@@ -620,14 +613,15 @@ function node_field_extra_fields() {
  * Deletes a node type from the database.
  *
  * @param $type
- *   The machine-readable name of the node type to be deleted.
+ *   The machine-readable name of the node type or the node type object itself
+ *   to be deleted.
  */
 function node_type_delete($type) {
-  $info = node_type_get_type($type);
+  $info = is_string($type) ? node_type_get_type($type) : $type;
   db_delete('node_type')
-    ->condition('type', $type)
+    ->condition('type', $info->type)
     ->execute();
-  field_attach_delete_bundle('node', $type);
+  field_attach_delete_bundle('node', $info->type);
   module_invoke_all('node_type_delete', $info);
 
   // Clear the node type cache.
@@ -660,6 +654,8 @@ function node_type_update_nodes($old_typ
  * These two information sources are not synchronized during module installation
  * until node_types_rebuild() is called.
  *
+ * @param $rebuild
+ *  TRUE to rebuild node types. Equivalent to calling node_types_rebuild().
  * @return
  *   Associative array with two components:
  *   - names: Associative array of the names of node types, keyed by the type.
@@ -673,42 +669,67 @@ function node_type_update_nodes($old_typ
  *   implementations, but are still in the database. These are indicated in the
  *   type object by $type->disabled being set to TRUE.
  */
-function _node_types_build() {
-  $_node_types = &drupal_static(__FUNCTION__);
-  if (is_object($_node_types)) {
-    return $_node_types;
+function _node_types_build($rebuild = FALSE) {
+  if (!$rebuild) {
+    $_node_types = &drupal_static(__FUNCTION__);
+    if (is_object($_node_types)) {
+      return $_node_types;
+    }
   }
+
   $_node_types = (object)array('types' => array(), 'names' => array());
 
   $info_array = module_invoke_all('node_info');
-  foreach ($info_array as $type => $info) {
-    $info['type'] = $type;
-    $_node_types->types[$type] = node_type_set_defaults($info);
-    $_node_types->names[$type] = $info['name'];
+  foreach (module_implements('node_info') as $module) {
+    $info_array = module_invoke($module, 'node_info');
+    foreach ($info_array as $type => $info) {
+      $info['type'] = $type;
+      $_node_types->types[$type] = node_type_set_defaults($info);
+      $_node_types->types[$type]->defining_module = $module;
+      $_node_types->names[$type] = $info['name'];
+    }
   }
-  $type_result = db_select('node_type', 'nt')
+  $query = db_select('node_type', 'nt')
     ->addTag('translatable')
     ->addTag('node_type_access')
     ->fields('nt')
-    ->orderBy('nt.type', 'ASC')
-    ->execute();
-  foreach ($type_result as $type_object) {
+    ->orderBy('nt.type', 'ASC');
+  if (!$rebuild) {
+    $query->condition('disabled', 0);
+  }
+  foreach ($query->execute() as $type_object) {
+    $type_db = $type_object->type;
+    // Original disabled value.
+    $disabled = $type_object->disabled;
     // Check for node types from disabled modules and mark their types for removal.
     // Types defined by the node module in the database (rather than by a separate
     // module using hook_node_info) have a base value of 'node_content'. The isset()
     // check prevents errors on old (pre-Drupal 7) databases.
-    if (isset($type_object->base) && $type_object->base != 'node_content' && empty($info_array[$type_object->type])) {
+    if (isset($type_object->base) && $type_object->base != 'node_content' && empty($info_array[$type_db])) {
       $type_object->disabled = TRUE;
     }
-    if (!isset($_node_types->types[$type_object->type]) || $type_object->modified) {
-      $_node_types->types[$type_object->type] = $type_object;
-      $_node_types->names[$type_object->type] = $type_object->name;
+    if (isset($info_array[$type_db])) {
+      $type_object->disabled = FALSE;
+    }
+    if (!isset($_node_types->types[$type_db]) || $type_object->modified) {
+      $_node_types->types[$type_db] = $type_object;
+      $_node_types->names[$type_db] = $type_object->name;
 
-      if ($type_object->type != $type_object->orig_type) {
+      if ($type_db != $type_object->orig_type) {
         unset($_node_types->types[$type_object->orig_type]);
         unset($_node_types->names[$type_object->orig_type]);
       }
     }
+    $_node_types->types[$type_db]->disabled = $type_object->disabled;
+    $_node_types->types[$type_db]->disabled_changed = $disabled != $type_object->disabled;
+  }
+
+  if ($rebuild) {
+    foreach ($_node_types->types as $type => $type_object) {
+      if (!empty($type_object->is_new) || !empty($type_object->disabled_changed)) {
+        node_type_save($type_object);
+      }
+    }
   }
 
   asort($_node_types->names);
@@ -742,6 +763,7 @@ function node_type_set_defaults($info = 
     $type->custom = 0;
     $type->modified = 0;
     $type->locked = 1;
+    $type->disabled = 0;
     $type->is_new = 1;
 
     $type->has_title = 1;
@@ -757,6 +779,9 @@ function node_type_set_defaults($info = 
   if (!$new_type->has_title) {
     $new_type->title_label = '';
   }
+  if (empty($new_type->defining_module)) {
+    $new_type->defining_module = $new_type->base == 'node_content' ? 'node' : '';
+  }
   $new_type->orig_type = isset($info['type']) ? $info['type'] : '';
 
   return $new_type;

=== modified file 'modules/node/node.test'
--- modules/node/node.test	2010-09-29 14:08:54 +0000
+++ modules/node/node.test	2010-10-06 00:05:26 +0000
@@ -1149,6 +1149,76 @@ class NodeTypeTestCase extends DrupalWeb
 }
 
 /**
+ * Test node type customizations persistence.
+ */
+class NodeTypePersistenceTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Node type persist',
+      'description' => 'Ensures that node type customization survives module enabling and disabling.',
+      'group' => 'Node',
+    );
+  }
+
+  function testNodeTypeCustomizationPersistence() {
+    $web_user = $this->drupalCreateUser(array('bypass node access', 'administer content types', 'administer modules'));
+    $this->drupalLogin($web_user);
+    $poll_key = 'modules[Core][poll][enable]';
+    $poll_enable = array($poll_key => "1");
+    $poll_disable = array($poll_key => FALSE);
+
+    // Enable poll and verify that the node type is in the DB and is not
+    // disabled.
+    $this->drupalPost('admin/modules', $poll_enable, t('Save configuration'));
+    $disabled = db_query('SELECT disabled FROM {node_type} WHERE type = :type', array(':type' => 'poll'))->fetchField();
+    $this->assertNotIdentical($disabled, FALSE, t('Poll node type found in the database'));
+    $this->assertEqual($disabled, 0, t('Poll node type is not disabled'));
+    // Check that poll node type (uncustomized) shows up.
+    $this->drupalGet('node/add');
+    $this->assertText('poll', t('poll type is found on node/add'));
+    // Customize poll description.
+    $description = $this->randomName();
+    $edit = array('description' => $description);
+    $this->drupalPost('admin/structure/types/manage/poll', $edit, t('Save content type'));
+    // Check that poll node type customization shows up.
+    $this->drupalGet('node/add');
+    $this->assertText($description, t('Customized description found'));
+
+    // Disable poll and check that the node type gets disabled.
+    $this->drupalPost('admin/modules', $poll_disable, t('Save configuration'));
+    $disabled = db_query('SELECT disabled FROM {node_type} WHERE type = :type', array(':type' => 'poll'))->fetchField();
+    $this->assertEqual($disabled, 1, t('Poll node type is disabled'));
+    $this->drupalGet('node/add');
+    $this->assertNoText('poll', t('poll type is not found on node/add'));
+
+    // Reenable poll and check that the customization survived the module
+    // disable.
+    $this->drupalPost('admin/modules', $poll_enable, t('Save configuration'));
+    $disabled = db_query('SELECT disabled FROM {node_type} WHERE type = :type', array(':type' => 'poll'))->fetchField();
+    $this->assertNotIdentical($disabled, FALSE, t('Poll node type found in the database'));
+    $this->assertEqual($disabled, 0, t('Poll node type is not disabled'));
+    $this->drupalGet('node/add');
+    $this->assertText($description, t('Customized description found'));
+
+    // Disable and uninstall poll.
+    $this->drupalPost('admin/modules', $poll_disable, t('Save configuration'));
+    $edit = array('uninstall[poll]' => 'poll');
+    $this->drupalPost('admin/modules/uninstall', $edit, t('Uninstall'));
+    $this->drupalPost(NULL, array(), t('Uninstall'));
+    $disabled = db_query('SELECT disabled FROM {node_type} WHERE type = :type', array(':type' => 'poll'))->fetchField();
+    $this->assertTrue($disabled, t('Poll node type is in the database and is disabled'));
+    $this->drupalGet('node/add');
+    $this->assertNoText('poll', t('poll type is no longer found on node/add'));
+
+    // Reenable poll and check that the customization survived the module
+    // uninstall.
+    $this->drupalPost('admin/modules', $poll_enable, t('Save configuration'));
+    $this->drupalGet('node/add');
+    $this->assertText($description, t('Customized description is found even after uninstall and reenable.'));
+  }
+}
+
+/**
  * Rebuild the node_access table.
  */
 class NodeAccessRebuildTestCase extends DrupalWebTestCase {

=== modified file 'profiles/standard/standard.install'
--- profiles/standard/standard.install	2010-10-05 06:17:28 +0000
+++ profiles/standard/standard.install	2010-10-05 22:31:20 +0000
@@ -210,6 +210,7 @@ function standard_install() {
       'type' => 'page',
       'name' => st('Basic page'),
       'base' => 'node_content',
+      'defining_module' => 'node',
       'description' => st("Use <em>basic pages</em> for your static content, such as an 'About us' page."),
       'custom' => 1,
       'modified' => 1,
@@ -219,6 +220,7 @@ function standard_install() {
       'type' => 'article',
       'name' => st('Article'),
       'base' => 'node_content',
+      'defining_module' => 'node',
       'description' => st('Use <em>articles</em> for time-sensitive content like news, press releases or blog posts.'),
       'custom' => 1,
       'modified' => 1,

