Index: modules/search/search.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/search/search.admin.inc,v retrieving revision 1.11 diff -u -u -p -r1.11 search.admin.inc --- modules/search/search.admin.inc 29 Aug 2009 21:05:16 -0000 1.11 +++ modules/search/search.admin.inc 4 Sep 2009 14:00:21 -0000 @@ -9,15 +9,23 @@ /** * Menu callback: confirm wiping of the index. */ -function search_reindex_confirm() { - return confirm_form(array(), t('Are you sure you want to re-index the site?'), - 'admin/config/search/settings', t(' The search index is not cleared but systematically updated to reflect the new settings. Searching will continue to work but new content won\'t be indexed until all existing content has been re-indexed. This action cannot be undone.'), t('Re-index site'), t('Cancel')); +function search_clear_confirm() { + $form = search_status_form('content items that will be cleared'); + $form['#description'] = t("The search index is not cleared but systematically updated to reflect the new settings. Searching will continue to work but new content won't be indexed until all existing content has been re-indexed. This action cannot be undone."); + return confirm_form( + $form, + t('Are you sure you want to clear the search index?'), + 'admin/config/search/settings', + '', + t('Clear index'), + t('Cancel') + ); } /** - * Handler for wipe confirmation + * Handler for wipe confirmation. */ -function search_reindex_confirm_submit(&$form, &$form_state) { +function search_clear_confirm_submit(&$form, &$form_state) { if ($form['confirm']) { search_reindex(); drupal_set_message(t('The index will be rebuilt.')); @@ -48,30 +56,54 @@ function _search_get_module_names() { } /** - * Menu callback; displays the search module settings page. - * - * @ingroup forms - * @see system_settings_form() - * @see search_admin_settings_submit() - * @see search_admin_reindex_submit() + * Return the search status form, which is used by several of the search forms. */ -function search_admin_settings() { - // Collect some stats +function search_status_form($action = 'left to index') { + // Collect some stats. $remaining = 0; $total = 0; - foreach(variable_get('search_active_modules', array('node', 'user')) as $module) { + foreach (variable_get('search_active_modules', array('node', 'user')) as $module) { if ($status = module_invoke($module, 'search_status')) { $remaining += $status['remaining']; $total += $status['total']; } } - $count = format_plural($remaining, 'There is 1 item left to index.', 'There are @count items left to index.'); - $percentage = ((int)min(100, 100 * ($total - $remaining) / max(1, $total))) . '%'; - $status = '

' . t('%percentage of the site has been indexed.', array('%percentage' => $percentage)) . ' ' . $count . '

'; - $form['status'] = array('#type' => 'fieldset', '#title' => t('Indexing status')); - $form['status']['status'] = array('#markup' => $status); - $form['status']['wipe'] = array('#type' => 'submit', '#value' => t('Re-index site'), '#submit' => array('search_admin_reindex_submit')); + // Return the status form. + $count = format_plural($remaining, 'There is 1 item ' . $action . '.', 'There are @count items ' . $action . '.'); + $percentage = (int) min(100, 100 * ($total - $remaining) / max(1, $total)); + return array( + 'status' => array( + '#type' => 'fieldset', + 'progress' => array( + '#type' => 'markup', + '#markup' => theme('progress_bar', $percentage, $count), + ), + ), + ); +} + +/** + * Menu callback; displays the search module settings page. + * + * @ingroup forms + * @see system_settings_form() + * @see search_admin_settings_submit() + * @see search_admin_clear_submit() + * @see search_admin_index_submit() + */ +function search_admin_settings() { + $form = search_status_form(); + $form['status']['wipe'] = array( + '#type' => 'submit', + '#value' => t('Wipe index'), + '#submit' => array('search_admin_clear_submit'), + ); + $form['status']['build'] = array( + '#type' => 'submit', + '#value' => t('Build index now'), + '#submit' => array('search_admin_index_submit'), + ); $items = drupal_map_assoc(array(10, 20, 50, 100, 200, 500)); @@ -156,9 +188,17 @@ function search_admin_settings_submit($f } /** - * Submit callback. + * Submit callback to wipe the search index. */ -function search_admin_reindex_submit($form, &$form_state) { +function search_admin_clear_submit($form, &$form_state) { // send the user to the confirmation page - $form_state['redirect'] = 'admin/config/search/settings/reindex'; -} \ No newline at end of file + $form_state['redirect'] = 'admin/config/search/settings/clear'; +} + +/** + * Submit callback to index the search index. + */ +function search_admin_index_submit($form, &$form_state) { + // send the user to the confirmation page + $form_state['redirect'] = 'admin/config/search/settings/index'; +} Index: modules/search/search.batch.inc =================================================================== RCS file: modules/search/search.batch.inc diff -N modules/search/search.batch.inc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modules/search/search.batch.inc 4 Sep 2009 14:00:21 -0000 @@ -0,0 +1,132 @@ + 0 ORDER BY d.reindex ASC, n.nid ASC"); + $nids = array(); + $limit = (int) variable_get('search_cron_limit', 100); + while ($node = db_fetch_object($results)) { + $nids[] = $node->nid; + if (count($nids) >= $limit) { + $operations[] = array('search_batch_index', array($nids)); + $nids = array(); + } + } + if (count($nids)) { + $operations[] = array('search_batch_index', array($nids)); + } + // When done with all the indexing, update the totals. + $operations[] = array('search_batch_done', array()); + + // when done redirect to the settings page. + $form_state['redirect'] = 'admin/config/search/settings'; + + // start the batch + $batch = array( + 'title' => t('Building Search Index'), + 'operations' => $operations, + 'finished' => 'search_batch_finished', + 'file' => drupal_get_path('module', 'search') . '/search.batch.inc', + 'progress_message' => t('Completed @current of @total.') . ' '. l(t('Cancel'), $form_state['redirect']), + ); + batch_set($batch); + } +} + +/** + * Build a form for cancelling the batch search index build. + */ +function search_batch_stop_form() { + $form = array(); + $form['stop'] = array('#type' => 'button', '#value' => t('Stop')); + return $form; +} + +/** + * Batch callback to index a set of nodes. + */ +function search_batch_index($nids, &$context) { + if (!isset($context['results'])) { + $context['results'] = array('attempted' => 0, 'success' => 0); + } + + foreach ($nids as $nid) { + if (isset($_SESSION['batch-search-stop'])) { + // @TODO: how do we stop batch api? + return; + } + $context['results']['attempted'] ++; + + // Build the node body. + $node = node_load($nid); + $node = node_build_content($node, 'search_index'); + $node->rendered = drupal_render($node->content); + + $text = '

'. check_plain($node->title) .'

'. $node->rendered; + + // Fetch extra data normally not visible + $extra = module_invoke_all('node_update_index', $node); + foreach ($extra as $t) { + $text .= $t; + } + + search_index($node->nid, 'node', $text); + $context['results']['success'] ++; + } +} + +/** + * Batch callback to update the search totals after the last search build is completed. + */ +function search_batch_done(&$context) { + // Update word IDF (Inverse Document Frequency) counts for all words + db_query("DELETE FROM {search_total}"); + db_query("INSERT INTO {search_total} (word, count) SELECT word, LOG10(1+1/GREATEST(1, SUM(score))) FROM {search_index} GROUP BY word"); +} + +/** + * Batch callback when search building is finished. + */ +function search_batch_finished($success, $results, $operations) { + unset($_SESSION['search-batch-stop']); + if ($success) { + if ($results['success'] == $results['attempted']) { + $message = t('Finished indexing @success nodes successfully.', array('@success' => $results['success'])); + } + else { + $message = t('Finished indexing @success of @attempted successfully.', array('@success' => $results['success'], '@attempt' => $results['attempted'])); + } + } + else { + $message = t('Finished with an error.'); + } + drupal_set_message($message); +} Index: modules/search/search.module =================================================================== RCS file: /cvs/drupal/drupal/modules/search/search.module,v retrieving revision 1.312 diff -u -u -p -r1.312 search.module --- modules/search/search.module 31 Aug 2009 17:06:09 -0000 1.312 +++ modules/search/search.module 4 Sep 2009 14:00:21 -0000 @@ -201,14 +201,22 @@ function search_menu() { 'type' => MENU_NORMAL_ITEM, 'file' => 'search.admin.inc', ); - $items['admin/config/search/settings/reindex'] = array( + $items['admin/config/search/settings/clear'] = array( 'title' => 'Clear index', 'page callback' => 'drupal_get_form', - 'page arguments' => array('search_reindex_confirm'), + 'page arguments' => array('search_clear_confirm'), 'access arguments' => array('administer search'), 'type' => MENU_CALLBACK, 'file' => 'search.admin.inc', ); + $items['admin/config/search/settings/index'] = array( + 'title' => 'Build index', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('search_batch_index_confirm'), + 'access arguments' => array('administer search'), + 'type' => MENU_CALLBACK, + 'file' => 'search.batch.inc', + ); $items['admin/reports/search'] = array( 'title' => 'Top search phrases', 'description' => 'View most popular search phrases.',