? search.patch Index: modules/node/node.module =================================================================== RCS file: /cvs/drupal/drupal/modules/node/node.module,v retrieving revision 1.776.2.22 diff -u -p -r1.776.2.22 node.module --- modules/node/node.module 7 Jan 2008 01:31:26 -0000 1.776.2.22 +++ modules/node/node.module 7 Jul 2008 09:13:59 -0000 @@ -807,153 +807,6 @@ function node_perm() { } /** - * Implementation of hook_search(). - */ -function node_search($op = 'search', $keys = NULL) { - switch ($op) { - case 'name': - return t('Content'); - - case 'reset': - variable_del('node_cron_last'); - variable_del('node_cron_last_nid'); - return; - - 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); - - 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' => ''. 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.') .''); - - $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)); - } - return $form; - - case 'search': - // Build matching conditions - list($join1, $where1) = _db_rewrite_sql(); - $arguments1 = array(); - $conditions1 = 'n.status = 1'; - - if ($type = search_query_extract($keys, 'type')) { - $types = array(); - foreach (explode(',', $type) as $t) { - $types[] = "n.type = '%s'"; - $arguments1[] = $t; - } - $conditions1 .= ' AND ('. implode(' OR ', $types) .')'; - $keys = search_query_insert($keys, 'type'); - } - - if ($category = search_query_extract($keys, 'category')) { - $categories = array(); - foreach (explode(',', $category) as $c) { - $categories[] = "tn.tid = %d"; - $arguments1[] = $c; - } - $conditions1 .= ' AND ('. implode(' OR ', $categories) .')'; - $join1 .= ' INNER JOIN {term_node} tn ON n.nid = tn.nid'; - $keys = search_query_insert($keys, 'category'); - } - - // Build ranking expression (we try to map each parameter to a - // uniform distribution in the range 0..1). - $ranking = array(); - $arguments2 = array(); - $join2 = ''; - // Used to avoid joining on node_comment_statistics twice - $stats_join = FALSE; - $total = 0; - if ($weight = (int)variable_get('node_rank_relevance', 5)) { - // Average relevance values hover around 0.15 - $ranking[] = '%d * i.relevance'; - $arguments2[] = $weight; - $total += $weight; - } - if ($weight = (int)variable_get('node_rank_recent', 5)) { - // Exponential decay with half-life of 6 months, starting at last indexed node - $ranking[] = '%d * POW(2, (GREATEST(n.created, n.changed, c.last_comment_timestamp) - %d) * 6.43e-8)'; - $arguments2[] = $weight; - $arguments2[] = (int)variable_get('node_cron_last', 0); - $join2 .= ' INNER JOIN {node} n ON n.nid = i.sid LEFT JOIN {node_comment_statistics} c ON c.nid = i.sid'; - $stats_join = TRUE; - $total += $weight; - } - if (module_exists('comment') && $weight = (int)variable_get('node_rank_comments', 5)) { - // Inverse law that maps the highest reply count on the site to 1 and 0 to 0. - $scale = variable_get('node_cron_comments_scale', 0.0); - $ranking[] = '%d * (2.0 - 2.0 / (1.0 + c.comment_count * %f))'; - $arguments2[] = $weight; - $arguments2[] = $scale; - if (!$stats_join) { - $join2 .= ' LEFT JOIN {node_comment_statistics} c ON c.nid = i.sid'; - } - $total += $weight; - } - if (module_exists('statistics') && variable_get('statistics_count_content_views', 0) && - $weight = (int)variable_get('node_rank_views', 5)) { - // Inverse law that maps the highest view count on the site to 1 and 0 to 0. - $scale = variable_get('node_cron_views_scale', 0.0); - $ranking[] = '%d * (2.0 - 2.0 / (1.0 + nc.totalcount * %f))'; - $arguments2[] = $weight; - $arguments2[] = $scale; - $join2 .= ' LEFT JOIN {node_counter} nc ON nc.nid = i.sid'; - $total += $weight; - } - $select2 = (count($ranking) ? implode(' + ', $ranking) : 'i.relevance') .' AS score'; - - // Do search - $find = do_search($keys, 'node', 'INNER JOIN {node} n ON n.nid = i.sid '. $join1 .' INNER JOIN {users} u ON n.uid = u.uid', $conditions1 . (empty($where1) ? '' : ' AND '. $where1), $arguments1, $select2, $join2, $arguments2); - - // Load results - $results = array(); - foreach ($find as $item) { - // Build the node body. - $node = node_load($item->sid); - $node = node_build_content($node, FALSE, FALSE); - $node->body = drupal_render($node->content); - - // Fetch comments for snippet - $node->body .= module_invoke('comment', 'nodeapi', $node, 'update index'); - // Fetch terms for snippet - $node->body .= module_invoke('taxonomy', 'nodeapi', $node, 'update index'); - - $extra = node_invoke_nodeapi($node, 'search result'); - $results[] = array('link' => url('node/'. $item->sid, NULL, NULL, TRUE), - 'type' => node_get_types('name', $node), - 'title' => $node->title, - 'user' => theme('username', $node), - 'date' => $node->changed, - 'node' => $node, - 'extra' => $extra, - 'score' => $item->score / $total, - 'snippet' => search_excerpt($keys, $node->body)); - } - return $results; - } -} - -/** * Implementation of hook_user(). */ function node_user($op, &$edit, &$user) { @@ -963,23 +816,6 @@ function node_user($op, &$edit, &$user) } } -function theme_node_search_admin($form) { - $output = drupal_render($form['info']); - - $header = array(t('Factor'), t('Weight')); - foreach (element_children($form['factors']) as $key) { - $row = array(); - $row[] = $form['factors'][$key]['#title']; - unset($form['factors'][$key]['#title']); - $row[] = drupal_render($form['factors'][$key]); - $rows[] = $row; - } - $output .= theme('table', $header, $rows); - - $output .= drupal_render($form); - return $output; -} - /** * Menu callback; presents general node configuration options. */ @@ -1097,14 +933,6 @@ function node_menu($may_cache) { $items[] = array('path' => 'admin/content/node/overview', 'title' => t('List'), 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10); - if (module_exists('search')) { - $items[] = array('path' => 'admin/content/search', 'title' => t('Search content'), - 'description' => t('Search content by keyword.'), - 'callback' => 'node_admin_search', - 'access' => user_access('administer nodes'), - 'type' => MENU_NORMAL_ITEM); - } - $items[] = array( 'path' => 'admin/content/node-settings', 'title' => t('Post settings'), @@ -1821,10 +1649,6 @@ function node_revision_list($node) { return $revisions; } -function node_admin_search() { - return drupal_get_form('search_form', url('admin/content/search'), $_POST['keys'], 'node') . search_data($_POST['keys'], 'node'); -} - /** * Implementation of hook_block(). */ @@ -2376,10 +2200,6 @@ function node_delete($nid) { // Clear the cache so an anonymous poster can see the node being deleted. cache_clear_all(); - // Remove this node from the search index if needed. - if (function_exists('search_wipe')) { - search_wipe($node->nid, 'node'); - } drupal_set_message(t('%title has been deleted.', array('%title' => $node->title))); watchdog('content', t('@type: deleted %title.', array('@type' => t($node->type), '%title' => $node->title))); } @@ -2500,156 +2320,6 @@ function node_update_shutdown() { } /** - * Implementation of hook_update_index(). - */ -function node_update_index() { - global $last_change, $last_nid; - - register_shutdown_function('node_update_shutdown'); - - $last = variable_get('node_cron_last', 0); - $last_nid = variable_get('node_cron_last_nid', 0); - $limit = (int)variable_get('search_cron_limit', 100); - - // 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); - - while ($node = db_fetch_object($result)) { - $last_change = $node->last_change; - $last_nid = $node->nid; - $node = node_load($node->nid); - - // Build the node body. - $node = node_build_content($node, FALSE, FALSE); - $node->body = drupal_render($node->content); - - // Allow modules to modify the fully-built node. - node_invoke_nodeapi($node, 'alter'); - - $text = '

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

'. $node->body; - - // Fetch extra data normally not visible - $extra = node_invoke_nodeapi($node, 'update index'); - foreach ($extra as $t) { - $text .= $t; - } - - // Update index - search_index($node->nid, 'node', $text); - } -} - -/** - * Implementation of hook_form_alter(). - */ -function node_form_alter($form_id, &$form) { - // Advanced node search form - if ($form_id == 'search_form' && arg(1) == 'node' && user_access('use advanced search')) { - // Keyword boxes: - $form['advanced'] = array( - '#type' => 'fieldset', - '#title' => t('Advanced search'), - '#collapsible' => TRUE, - '#collapsed' => TRUE, - '#attributes' => array('class' => 'search-advanced'), - ); - $form['advanced']['keywords'] = array( - '#prefix' => '
', - '#suffix' => '
', - ); - $form['advanced']['keywords']['or'] = array( - '#type' => 'textfield', - '#title' => t('Containing any of the words'), - '#size' => 30, - '#maxlength' => 255, - ); - $form['advanced']['keywords']['phrase'] = array( - '#type' => 'textfield', - '#title' => t('Containing the phrase'), - '#size' => 30, - '#maxlength' => 255, - ); - $form['advanced']['keywords']['negative'] = array( - '#type' => 'textfield', - '#title' => t('Containing none of the words'), - '#size' => 30, - '#maxlength' => 255, - ); - - // Taxonomy box: - if ($taxonomy = module_invoke('taxonomy', 'form_all', 1)) { - $form['advanced']['category'] = array( - '#type' => 'select', - '#title' => t('Only in the category(s)'), - '#prefix' => '
', - '#size' => 10, - '#suffix' => '
', - '#options' => $taxonomy, - '#multiple' => TRUE, - ); - } - - // Node types: - $types = array_map('check_plain', node_get_types('names')); - $form['advanced']['type'] = array( - '#type' => 'checkboxes', - '#title' => t('Only of the type(s)'), - '#prefix' => '
', - '#suffix' => '
', - '#options' => $types, - ); - $form['advanced']['submit'] = array( - '#type' => 'submit', - '#value' => t('Advanced search'), - '#prefix' => '
', - '#suffix' => '
', - ); - - $form['#validate']['node_search_validate'] = array(); - } -} - -/** - * Form API callback for the search form. Registered in node_form_alter(). - */ -function node_search_validate($form_id, $form_values, $form) { - // Initialise using any existing basic search keywords. - $keys = $form_values['processed_keys']; - - // Insert extra restrictions into the search keywords string. - if (isset($form_values['type']) && is_array($form_values['type'])) { - // Retrieve selected types - Forms API sets the value of unselected checkboxes to 0. - $form_values['type'] = array_filter($form_values['type']); - if (count($form_values['type'])) { - $keys = search_query_insert($keys, 'type', implode(',', array_keys($form_values['type']))); - } - } - - if (isset($form_values['category']) && is_array($form_values['category'])) { - $keys = search_query_insert($keys, 'category', implode(',', $form_values['category'])); - } - if ($form_values['or'] != '') { - if (preg_match_all('/ ("[^"]+"|[^" ]+)/i', ' '. $form_values['or'], $matches)) { - $keys .= ' '. implode(' OR ', $matches[1]); - } - } - if ($form_values['negative'] != '') { - if (preg_match_all('/ ("[^"]+"|[^" ]+)/i', ' '. $form_values['negative'], $matches)) { - $keys .= ' -'. implode(' -', $matches[1]); - } - } - if ($form_values['phrase'] != '') { - $keys .= ' "'. str_replace('"', ' ', $form_values['phrase']) .'"'; - } - if (!empty($keys)) { - form_set_value($form['basic']['inline']['processed_keys'], trim($keys)); - } -} - -/** * @defgroup node_access Node access rights * @{ * The node access system determines who can do what to which nodes. Index: modules/search/search.module =================================================================== RCS file: /cvs/drupal/drupal/modules/search/search.module,v retrieving revision 1.209.2.5 diff -u -p -r1.209.2.5 search.module --- modules/search/search.module 26 Jul 2007 19:16:48 -0000 1.209.2.5 +++ modules/search/search.module 7 Jul 2008 09:13:59 -0000 @@ -168,7 +168,8 @@ function search_menu($may_cache) { // we dynamically add the keywords to the search tabs' paths. $keys = search_get_keys(); $keys = strlen($keys) ? '/'. $keys : ''; - foreach (module_list() as $name) { + $searches = search_get_implementing_modules(); + foreach ($searches as $name) { if (module_hook($name, 'search') && $title = module_invoke($name, 'search', 'name')) { $items[] = array('path' => 'search/'. $name . $keys, 'title' => $title, 'callback' => 'search_view', @@ -181,6 +182,16 @@ function search_menu($may_cache) { return $items; } +function search_get_implementing_modules() { + $modules = array(); + foreach (module_list(FALSE, TRUE, TRUE) as $name) { + if (module_hook($name, 'search')) { + $modules[] = $name; + } + } + return $modules; +} + /** * Validate callback. */ @@ -442,7 +453,7 @@ function search_preprocess(&$text) { * A number identifying this particular item (e.g. node id). * * @param $type - * A string defining this type of item (e.g. 'node') + * A string defining this type of item (e.g. 'contentsearch') * * @param $text * The content of this item. Must be a piece of HTML text. @@ -897,10 +908,16 @@ function search_view() { // search/type/keyword+keyword if (!isset($_POST['form_id'])) { if ($type == '') { - // Note: search/node can not be a default tab because it would take on the + // Note: search/contentsearch can not be a default tab because it would take on the // path of its parent (search). It would prevent remembering keywords when // switching tabs. This is why we drupal_goto to it from the parent instead. - drupal_goto('search/node'); + if ($default = array_shift(search_get_implementing_modules())) { + drupal_goto('search/'. $default); + } + else { + drupal_set_message(t('There are no search modules enabled.'), 'error'); + return ''; + } } $keys = search_get_keys(); @@ -1026,8 +1043,8 @@ function search_form_submit($form_id, $f form_set_error('keys', t('Please enter some keywords.')); // Fall through to the drupal_goto() call. } - - $type = $form_values['module'] ? $form_values['module'] : 'node'; + $default_type = array_shift(search_get_implementing_modules()); + $type = $form_values['module'] ? $form_values['module'] : $default_type; return 'search/'. $type .'/'. $keys; } @@ -1045,7 +1062,8 @@ function search_box($form_id) { $form['submit'] = array('#type' => 'submit', '#value' => t('Search')); // Always go to the search page since the search form is not guaranteed to be // on every page. - $form['#action'] = url('search/node'); + $type = array_shift(search_get_implementing_modules()); + $form['#action'] = url('search/'. $type); $form['#base'] = 'search_box_form'; return $form; @@ -1055,7 +1073,8 @@ function search_box($form_id) { * Process a block search form submission. */ function search_box_form_submit($form_id, $form_values) { - return 'search/node/'. trim($form_values[$form_id .'_keys']); + $type = array_shift(search_get_implementing_modules()); + return "search/$type/". trim($form_values[$form_id .'_keys']); } /** @@ -1075,8 +1094,10 @@ function theme_search_block_form($form) /** * Perform a standard search on the given keys, and return the formatted results. */ -function search_data($keys = NULL, $type = 'node') { - +function search_data($keys = NULL, $type = NULL) { + if (is_null($type)) { + $type = array_shift(search_get_implementing_modules()); + } if (isset($keys)) { if (module_hook($type, 'search')) { $results = module_invoke($type, 'search', 'search', $keys); @@ -1225,7 +1246,7 @@ function _search_excerpt_replace(&$text) * Optionally, "extra" can be an array of extra info to show along with the * result. * @param $type - * The type of item found, such as "user" or "node". + * The type of item found, such as "user" or "contentsearch". * * @ingroup themeable */ @@ -1258,7 +1279,7 @@ function theme_search_item($item, $type) * @param $results * All search result as returned by hook_search(). * @param $type - * The type of item found, such as "user" or "node". + * The type of item found, such as "user" or "contentsearch". * * @ingroup themeable */ Index: modules/user/user.module =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.module,v retrieving revision 1.745.2.26 diff -u -p -r1.745.2.26 user.module --- modules/user/user.module 7 Jan 2008 02:30:35 -0000 1.745.2.26 +++ modules/user/user.module 7 Jul 2008 09:14:00 -0000 @@ -439,29 +439,6 @@ function user_file_download($file) { } /** - * Implementation of hook_search(). - */ -function user_search($op = 'search', $keys = NULL) { - switch ($op) { - case 'name': - if (user_access('access user profiles')) { - return t('Users'); - } - case 'search': - if (user_access('access user profiles')) { - $find = array(); - // Replace wildcards with MySQL/PostgreSQL wildcards. - $keys = preg_replace('!\*+!', '%', $keys); - $result = pager_query("SELECT name, uid FROM {users} WHERE LOWER(name) LIKE LOWER('%%%s%%')", 15, 0, NULL, $keys); - while ($account = db_fetch_object($result)) { - $find[] = array('title' => $account->name, 'link' => url('user/'. $account->uid, NULL, NULL, TRUE)); - } - return $find; - } - } -} - -/** * Implementation of hook_user(). */ function user_user($type, &$edit, &$user, $category = NULL) { @@ -2683,4 +2660,3 @@ function user_forms() { $forms['user_admin_new_role']['callback'] = 'user_admin_role'; return $forms; } -