=== modified file 'includes/common.inc'
--- includes/common.inc	2010-08-22 12:46:21 +0000
+++ includes/common.inc	2010-08-27 06:23:30 +0000
@@ -6309,8 +6309,8 @@ function drupal_flush_all_caches() {
   system_rebuild_theme_data();
   drupal_theme_rebuild();
 
-  menu_rebuild();
   node_types_rebuild();
+  menu_rebuild();
 
   // Don't clear cache_form - in-progress form submissions may break.
   // Ordered so clearing the page cache will always be the last action.

=== modified file 'includes/install.inc'
--- includes/install.inc	2010-08-22 15:31:18 +0000
+++ includes/install.inc	2010-08-27 09:37:21 +0000
@@ -628,6 +628,11 @@ function drupal_uninstall_modules($modul
 
     drupal_set_installed_schema_version($module, SCHEMA_UNINSTALLED);
   }
+  // Remove the node types defined by the module.
+  $node_types = db_query("SELECT * FROM {node_type} WHERE defining_module = :module", array(':module' => $module));
+  foreach ($node_types as $type_object) {
+    node_type_delete($type_object);
+  }
 
   if (!empty($module_list)) {
     // Call hook_module_uninstall to let other modules act

=== modified file 'modules/node/content_types.inc'
--- modules/node/content_types.inc	2010-08-08 13:02:37 +0000
+++ modules/node/content_types.inc	2010-08-27 07:51:07 +0000
@@ -317,6 +317,7 @@ function node_type_form_submit($form, &$
   $type->custom = $form_state['values']['custom'];
   $type->modified = TRUE;
   $type->locked = $form_state['values']['locked'];
+  $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-08-22 13:55:53 +0000
+++ modules/node/node.install	2010-08-27 06:27: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,12 @@ 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',

=== modified file 'modules/node/node.module'
--- modules/node/node.module	2010-08-23 22:15:34 +0000
+++ modules/node/node.module	2010-08-27 09:42:35 +0000
@@ -464,16 +464,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);
 }
 
 /**
@@ -502,7 +493,7 @@ function node_type_load($name) {
 function node_type_save($info) {
   $is_existing = FALSE;
   $existing_type = !empty($info->old_type) ? $info->old_type : $info->type;
-  $is_existing = (bool) db_query_range('SELECT 1 FROM {node_type} WHERE type = :type', 0, 1, array(':type' => $existing_type))->fetchField();
+  $is_existing = (bool) db_query_range('SELECT COUNT(*) FROM {node_type} WHERE type = :type', 0, 1, array(':type' => $existing_type))->fetchField();
   $type = node_type_set_defaults($info);
 
   $fields = array(
@@ -516,6 +507,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) {
@@ -623,14 +616,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.
@@ -676,42 +670,66 @@ 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;
+    $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);
@@ -745,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;

=== modified file 'modules/node/node.test'
--- modules/node/node.test	2010-08-22 16:11:12 +0000
+++ modules/node/node.test	2010-08-27 06:08:15 +0000
@@ -1141,6 +1141,76 @@ class NodeTypeTestCase extends DrupalWeb
 }
 
 /**
+ * Test node type customizations persist.
+ */
+class NodeTypePersistTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Node type persist',
+      'description' => 'Ensures that node type customization survive 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->assertIdentical($disabled, FALSE, t('Poll node type is not found in the database'));
+    $this->drupalGet('node/add');
+    $this->assertNoText('poll', t('poll type is no longer found on node/add'));
+
+    // Reenable poll and check that the customization did not survive the
+    // module uninstall.
+    $this->drupalPost('admin/modules', $poll_enable, t('Save configuration'));
+    $this->drupalGet('node/add');
+    $this->assertNoText($description, t('Customized description is not found'));
+  }
+}
+
+/**
  * Rebuild the node_access table.
  */
 class NodeAccessRebuildTestCase extends DrupalWebTestCase {

=== modified file 'modules/system/system.admin.inc'
--- modules/system/system.admin.inc	2010-08-22 12:55:04 +0000
+++ modules/system/system.admin.inc	2010-08-27 06:32:35 +0000
@@ -1294,7 +1294,8 @@ function system_modules_uninstall($form,
     // Load the .install file, and check for an uninstall or schema hook.
     // If the hook exists, the module can be uninstalled.
     module_load_install($module->name);
-    if (module_hook($module->name, 'uninstall') || module_hook($module->name, 'schema')) {
+    $node_type_count = db_query("SELECT COUNT(*) FROM {node_type} WHERE defining_module = :module", array(':module' => $module->name))->fetchField();
+    if (module_hook($module->name, 'uninstall') || module_hook($module->name, 'schema') || $node_type_count) {
       $form['modules'][$module->name]['name'] = array('#markup' => $info['name'] ? $info['name'] : $module->name);
       $form['modules'][$module->name]['description'] = array('#markup' => t($info['description']));
       $options[$module->name] = '';

=== modified file 'profiles/standard/standard.install'
--- profiles/standard/standard.install	2010-08-09 19:55:57 +0000
+++ profiles/standard/standard.install	2010-08-27 06:31:39 +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,

