### Eclipse Workspace Patch 1.0
#P Test Drupal 6
Index: modules/search/search.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/search/search.module,v
retrieving revision 1.226
diff -u -r1.226 search.module
--- modules/search/search.module	22 Jun 2007 08:32:28 -0000	1.226
+++ modules/search/search.module	22 Jun 2007 20:50:47 -0000
@@ -205,15 +205,27 @@
 }
 
 /**
- * Validate callback.
+ * Validate handler handler for search_admin_settings.
  */
 function search_admin_settings_validate($form, &$form_state) {
   if ($form_state['values']['op'] == t('Re-index site')) {
     drupal_goto('admin/settings/search/wipe');
   }
+
+  // Store the current value of the variables before system_theme_settings_submit
+  // updates them.
+  $form_state['storage']['prev_minimum_word_size'] = variable_get('minimum_word_size', 3);
+  $form_state['storage']['prev_overlap_cjk'] = variable_get('overlap_cjk', TRUE);
+}
+
+/**
+ * Submit handler handler for search_admin_settings.
+ * Gets executed after system_theme_settings_submit has updated the variables.
+ */
+function search_admin_settings_submit($form, &$form_state) {
   // If these settings change, the index needs to be rebuilt.
-  if ((variable_get('minimum_word_size', 3) != $form_state['values']['minimum_word_size']) ||
-      (variable_get('overlap_cjk', TRUE) != $form_state['values']['overlap_cjk'])) {
+  if ((variable_get('minimum_word_size', 3) != $form_state['storage']['prev_minimum_word_size']) ||
+      (variable_get('overlap_cjk', TRUE) != $form_state['storage']['prev_overlap_cjk'])) {
     drupal_set_message(t('The index will be rebuilt.'));
     search_wipe();
   }
@@ -251,11 +263,14 @@
   $form['indexing_settings']['minimum_word_size'] = array('#type' => 'textfield', '#title' => t('Minimum word length to index'), '#default_value' => variable_get('minimum_word_size', 3), '#size' => 5, '#maxlength' => 3, '#description' => t('The number of characters a word has to be to be indexed. A lower setting means better search result ranking, but also a larger database. Each search query must contain at least one keyword that is this size (or longer).'));
   $form['indexing_settings']['overlap_cjk'] = array('#type' => 'checkbox', '#title' => t('Simple CJK handling'), '#default_value' => variable_get('overlap_cjk', TRUE), '#description' => t('Whether to apply a simple Chinese/Japanese/Korean tokenizer based on overlapping sequences. Turn this off if you want to use an external preprocessor for this instead. Does not affect other languages.'));
 
-  $form['#validate'] = array('search_admin_settings_validate');
+  $form['#validate'][] = 'search_admin_settings_validate';
 
-  // Per module settings
-  $form = array_merge($form, module_invoke_all('search', 'admin'));
-  return system_settings_form($form);
+  $form = system_settings_form($form);
+
+  $form['#submit'][] = 'search_admin_settings_submit';
+
+
+  return $form;
 }
 
 /**
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.495
diff -u -r1.495 system.module
--- modules/system/system.module	22 Jun 2007 08:32:28 -0000	1.495
+++ modules/system/system.module	22 Jun 2007 20:50:50 -0000
@@ -1250,6 +1250,7 @@
  * Add default buttons to a form and set its prefix
  */
 function system_settings_form($form) {
+  $form['buttons'] = array('#weight' => 30);
   $form['buttons']['submit'] = array('#type' => 'submit', '#value' => t('Save configuration') );
   $form['buttons']['reset'] = array('#type' => 'submit', '#value' => t('Reset to defaults') );
 
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.835
diff -u -r1.835 node.module
--- modules/node/node.module	22 Jun 2007 08:32:27 -0000	1.835
+++ modules/node/node.module	22 Jun 2007 20:50:46 -0000
@@ -870,32 +870,18 @@
     case 'status':
       $last = variable_get('node_cron_last', 0);
       $last_nid = variable_get('node_cron_last_nid', 0);
-      $total = db_result(db_query('SELECT COUNT(*) FROM {node} WHERE status = 1'));
-      $remaining = db_result(db_query('SELECT COUNT(*) FROM {node} n LEFT JOIN {node_comment_statistics} c ON n.nid = c.nid WHERE n.status = 1 AND ((GREATEST(n.created, n.changed, c.last_comment_timestamp) = %d AND n.nid > %d ) OR (n.created > %d OR n.changed > %d OR c.last_comment_timestamp > %d))', $last, $last_nid, $last, $last, $last));
-      return array('remaining' => $remaining, 'total' => $total);
+      $excluded_types = variable_get('search_excluded_content_types', array());
 
-    case 'admin':
-      $form = array();
-      // Output form for defining rank factor weights.
-      $form['content_ranking'] = array('#type' => 'fieldset', '#title' => t('Content ranking'));
-      $form['content_ranking']['#theme'] = 'node_search_admin';
-      $form['content_ranking']['info'] = array('#value' => '<em>'. t('The following numbers control which properties the content search should favor when ordering the results. Higher numbers mean more influence, zero means the property is ignored. Changing these numbers does not require the search index to be rebuilt. Changes take effect immediately.') .'</em>');
-
-      $ranking = array('node_rank_relevance' => t('Keyword relevance'),
-                       'node_rank_recent' => t('Recently posted'));
-      if (module_exists('comment')) {
-        $ranking['node_rank_comments'] = t('Number of comments');
-      }
-      if (module_exists('statistics') && variable_get('statistics_count_content_views', 0)) {
-        $ranking['node_rank_views'] = t('Number of views');
-      }
-
-      // Note: reversed to reflect that higher number = higher ranking.
-      $options = drupal_map_assoc(range(0, 10));
-      foreach ($ranking as $var => $title) {
-        $form['content_ranking']['factors'][$var] = array('#title' => $title, '#type' => 'select', '#options' => $options, '#default_value' => variable_get($var, 5));
+      $args = array($last, $last_nid, $last, $last, $last);
+      $type_exclude = '';
+      if (!empty($excluded_types)) {
+        $type_exclude = 'n.type NOT IN('. implode(',', array_fill(0, count($excluded_types), "'%s'")) .') AND ';
+        $args = array_merge(array_values($excluded_types), $args);
       }
-      return $form;
+      $total = db_result(db_query("SELECT COUNT(*) FROM {node} n WHERE $type_exclude n.status = 1", $excluded_types));
+      $remaining = db_result(db_query("SELECT COUNT(*) FROM {node} n LEFT JOIN {node_comment_statistics} c ON n.nid = c.nid WHERE $type_exclude n.status = 1 AND ((GREATEST(n.created, n.changed, c.last_comment_timestamp) = %d AND n.nid > %d ) OR (n.created > %d OR n.changed > %d OR c.last_comment_timestamp > %d))", $args));
+
+      return array('remaining' => $remaining, 'total' => $total);
 
     case 'search':
       // Build matching conditions
@@ -1002,6 +988,48 @@
 }
 
 /**
+ * Validate handler for search_admin_settings - added through hook_form_alter.
+ */
+function node_search_admin_settings_validate($form, &$form_state) {
+  // What we actually store is the list of excluded content types.
+  $included_types = $form_state['values']['search_indexed_content_types'];
+  $excluded_types = drupal_map_assoc(array_diff(array_keys(node_get_types('names')), $included_types));
+  unset($form_state['values']['search_indexed_content_types']);
+  $form_state['values']['search_excluded_content_types'] = $excluded_types;
+
+  // Store the current value of the variable before system_theme_settings_submit
+  // updates it.
+  $form_state['form']['prev_search_excluded_content_types'] = variable_get('search_excluded_content_types', array());
+}
+
+/**
+ * Submit handler for search_admin_settings - added through hook_form_alter.
+ * Gets executed after system_theme_settings_submit has updated the variables.
+ */
+function node_search_admin_settings_submit($form, &$form_state) {
+  $new_excluded_types = variable_get('search_excluded_content_types', array());
+  $old_excluded_types = $form_state['form']['prev_search_excluded_content_types'];
+
+  // Wipe indexed data for the newly excluded content types
+  $newly_excluded = array_diff($new_excluded_types, $old_excluded_types);
+  if (!empty($newly_excluded)) {
+    $result = db_query('SELECT nid FROM {node} WHERE type IN('. implode(',', array_fill(0, count($newly_excluded), "'%s'")) .')', $newly_excluded);
+    while ($row = db_fetch_array($result)) {
+      search_wipe($row['nid'], 'node');
+    }
+  }
+
+  // If some content types were re-included, the whole index will need to be rebuilt.
+  $newly_included = array_diff($old_excluded_types, $new_excluded_types);
+  if (!empty($newly_included)) {
+    drupal_set_message(t('The index will be rebuilt.'));
+    search_wipe();
+  }
+
+
+}
+
+/**
  * Implementation of hook_user().
  */
 function node_user($op, &$edit, &$user) {
@@ -2598,12 +2627,19 @@
   $last = variable_get('node_cron_last', 0);
   $last_nid = variable_get('node_cron_last_nid', 0);
   $limit = (int)variable_get('search_cron_limit', 100);
+  $excluded_types = variable_get('search_excluded_content_types', array());
 
   // Store the maximum possible comments per thread (used for ranking by reply count)
   variable_set('node_cron_comments_scale', 1.0 / max(1, db_result(db_query('SELECT MAX(comment_count) FROM {node_comment_statistics}'))));
   variable_set('node_cron_views_scale', 1.0 / max(1, db_result(db_query('SELECT MAX(totalcount) FROM {node_counter}'))));
 
-  $result = db_query_range('SELECT GREATEST(IF(c.last_comment_timestamp IS NULL, 0, c.last_comment_timestamp), n.changed) as last_change, n.nid FROM {node} n LEFT JOIN {node_comment_statistics} c ON n.nid = c.nid WHERE n.status = 1 AND ((GREATEST(n.changed, c.last_comment_timestamp) = %d AND n.nid > %d) OR (n.changed > %d OR c.last_comment_timestamp > %d)) ORDER BY GREATEST(n.changed, c.last_comment_timestamp) ASC, n.nid ASC', $last, $last_nid, $last, $last, $last, 0, $limit);
+  $args = array($last, $last_nid, $last, $last, $last);
+  $type_exclude = '';
+  if (!empty($excluded_types)) {
+    $type_exclude = 'n.type NOT IN('. implode(',', array_fill(0, count($excluded_types), "'%s'")) .') AND ';
+    $args = array_merge(array_values($excluded_types), $args);
+  }
+  $result = db_query_range("SELECT GREATEST(IF(c.last_comment_timestamp IS NULL, 0, c.last_comment_timestamp), n.changed) as last_change, n.nid FROM {node} n LEFT JOIN {node_comment_statistics} c ON n.nid = c.nid WHERE $type_exclude n.status = 1 AND ((GREATEST(n.changed, c.last_comment_timestamp) = %d AND n.nid > %d) OR (n.changed > %d OR c.last_comment_timestamp > %d)) ORDER BY GREATEST(n.changed, c.last_comment_timestamp) ASC, n.nid ASC", $args, 0, $limit);
 
   while ($node = db_fetch_object($result)) {
     $last_change = $node->last_change;
@@ -2679,6 +2715,9 @@
 
     // Node types:
     $types = node_get_types('names');
+    foreach (variable_get('search_excluded_content_types', array()) as $excluded_type) {
+      unset($types[$excluded_type]);
+    }
     $form['advanced']['type'] = array(
       '#type' => 'checkboxes',
       '#title' => t('Only of the type(s)'),
@@ -2695,6 +2734,49 @@
 
     $form['#validate'][] = 'node_search_validate';
   }
+  // Search settings form
+  elseif ($form_id == 'search_admin_settings') {
+    $form['content_indexing'] = array('#type' => 'fieldset', '#title' => t('Content indexing settings'));
+    $form['content_indexing']['info'] = array('#value' => '<em>'. t('Changing the settings below can cause the site index to be rebuilt.') .'</em>');
+
+    // Content indexing is opt-out : the actual setting is a list of
+    // excluded content types, so that newly added content types are
+    // indexed by default.
+    // However, to keep the UI simple, we let the user select the content
+    // types that should be indexed, and store the complement.
+    $excluded_types = variable_get('search_excluded_content_types', array());
+    $included_types = drupal_map_assoc(array_diff(array_keys(node_get_types('names')), $excluded_types));
+    $form['content_indexing']['search_indexed_content_types'] = array(
+      '#type' => 'checkboxes',
+      '#multiple' => TRUE,
+      '#title' => t('Indexed content types'),
+      '#options' => node_get_types('names'),
+      '#default_value' => $included_types,
+      '#description' => t('Unselect a content type to have it excluded from search results.<br/>Excluding a content type will wipe the search index from the corresponding data.<br/>Re-including a content type will cause the whole site index to be rebuilt.'),
+    );
+
+    // Output form for defining rank factor weights.
+    $form['content_ranking'] = array('#type' => 'fieldset', '#title' => t('Content ranking'));
+    $form['content_ranking']['#theme'] = 'node_search_admin';
+    $form['content_ranking']['info'] = array('#value' => '<em>'. t('The following numbers control which properties the content search should favor when ordering the results. Higher numbers mean more influence, zero means the property is ignored. Changing these numbers does not require the search index to be rebuilt. Changes take effect immediately.') .'</em>');
+
+    $ranking = array('node_rank_relevance' => t('Keyword relevance'),
+                     'node_rank_recent' => t('Recently posted'));
+    if (module_exists('comment')) {
+      $ranking['node_rank_comments'] = t('Number of comments');
+    }
+    if (module_exists('statistics') && variable_get('statistics_count_content_views', 0)) {
+      $ranking['node_rank_views'] = t('Number of views');
+    }
+
+    // Note: reversed to reflect that higher number = higher ranking.
+    $options = drupal_map_assoc(range(0, 10));
+    foreach ($ranking as $var => $title) {
+      $form['content_ranking']['factors'][$var] = array('#title' => $title, '#type' => 'select', '#options' => $options, '#default_value' => variable_get($var, 5));
+    }
+    $form['#validate'][] = 'node_search_admin_settings_validate';
+    $form['#submit'][] = 'node_search_admin_settings_submit';
+  }
 }
 
 /**
