Index: modules/search/search.pages.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/search/search.pages.inc,v retrieving revision 1.11 diff -u -r1.11 search.pages.inc --- modules/search/search.pages.inc 25 Aug 2009 10:27:14 -0000 1.11 +++ modules/search/search.pages.inc 5 Oct 2009 19:48:56 -0000 @@ -10,6 +10,8 @@ * Menu callback; presents the search form and/or search results. */ function search_view($type = 'node') { + global $language; + // Search form submits with POST but redirects to GET. This way we can keep // the search query URL clean as a whistle: // search/type/keyword+keyword @@ -29,7 +31,7 @@ watchdog('search', '%keys (@type).', array('%keys' => $keys, '@type' => module_invoke($type, 'search', 'name')), WATCHDOG_NOTICE, l(t('results'), 'search/' . $type . '/' . $keys)); // Collect the search results: - $results = search_data($keys, $type); + $results = search_data($keys, $type, $language->language); // Construct the search form. $build['search_form'] = drupal_get_form('search_form', NULL, $keys, $type); Index: modules/search/search.module =================================================================== RCS file: /cvs/drupal/drupal/modules/search/search.module,v retrieving revision 1.318 diff -u -r1.318 search.module --- modules/search/search.module 21 Sep 2009 06:44:14 -0000 1.318 +++ modules/search/search.module 5 Oct 2009 19:48:56 -0000 @@ -353,8 +353,15 @@ /** * Simplifies a string according to indexing rules. + * + * @param $text + * Text to simplify. + * @param $lang + * Language the text is in, e.g. 'de' or 'en-US'; NULL if unknown/multiple. + * @return + * Simplified version of $text. */ -function search_simplify($text) { +function search_simplify($text, $lang = NULL) { // Decode entities to UTF-8 $text = decode_entities($text); @@ -362,7 +369,9 @@ $text = drupal_strtolower($text); // Call an external processor for word handling. - search_invoke_preprocess($text); + foreach (module_implements('search_preprocess') as $module) { + $text = module_invoke($module, 'search_preprocess', $text, $lang); + } // Simple CJK handling if (variable_get('overlap_cjk', TRUE)) { @@ -418,9 +427,16 @@ } /** - * Splits a string into tokens for indexing. + * Preprocesses and splits a string into tokens for indexing. + * + * @param $text + * Text to simplify. + * @param $lang + * Language the text is in, e.g. 'de' or 'en-US'; NULL if unknown/multiple. + * @return + * Simplified version of $text. */ -function search_index_split($text) { +function search_index_split($text, $lang = NULL) { static $last = NULL; static $lastsplit = NULL; @@ -428,7 +444,7 @@ return $lastsplit; } // Process words - $text = search_simplify($text); + $text = search_simplify($text, $lang); $words = explode(' ', $text); array_walk($words, '_search_index_truncate'); @@ -447,29 +463,20 @@ } /** - * Invokes hook_search_preprocess() in modules. - */ -function search_invoke_preprocess(&$text) { - foreach (module_implements('search_preprocess') as $module) { - $text = module_invoke($module, 'search_preprocess', $text); - } -} - -/** * Update the full-text search index for a particular item. * * @param $sid * A number identifying this particular item (e.g. node id). - * * @param $type * A string defining this type of item (e.g. 'node') - * * @param $text * The content of this item. Must be a piece of HTML text. + * @param $lang + * Language the text is in, e.g. 'de' or 'en-US'; NULL if unknown/multiple. * * @ingroup search */ -function search_index($sid, $type, $text) { +function search_index($sid, $type, $text, $lang = NULL) { $minimum_word_size = variable_get('minimum_word_size', 3); // Link matching @@ -574,7 +581,7 @@ $value = $linktitle; } } - $words = search_index_split($value); + $words = search_index_split($value, $lang); foreach ($words as $word) { // Add word to accumulator $accum .= $word . ' '; @@ -953,13 +960,23 @@ } /** - * Perform a standard search on the given keys, and return the formatted results. + * Performs a keyword search by invoking hook_search_execute(). + * + * @param $keys + * Keywords being searched for. + * @param $type + * Type of search requested. + * @param $lang + * The language the keywords are presumed to be in, e.g. 'de' or 'en-US'; + * NULL if unknown/multiple. + * @return + * Themed and formatted page of search results. */ -function search_data($keys = NULL, $type = 'node') { +function search_data($keys = NULL, $type = 'node', $lang = NULL) { if (isset($keys)) { if (module_hook($type, 'search_execute')) { - $results = module_invoke($type, 'search_execute', $keys); + $results = module_invoke($type, 'search_execute', $keys, $lang); if (isset($results) && is_array($results) && count($results)) { if (module_hook($type, 'search_page')) { return module_invoke($type, 'search_page', $results); Index: modules/search/search.extender.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/search/search.extender.inc,v retrieving revision 1.1 diff -u -r1.1 search.extender.inc --- modules/search/search.extender.inc 29 Aug 2009 12:43:18 -0000 1.1 +++ modules/search/search.extender.inc 5 Oct 2009 19:48:56 -0000 @@ -122,18 +122,33 @@ protected $multiply = array(); /** + * Language of search terms, e.g. 'de' or 'en-US'; NULL if unknown. + * + * This is passed to search_simplify() for language-specific preprocessing + * of keywords. + * + * @var string + */ + protected $term_lang = NULL; + + /** * Search items for the given search query string and type. * * @param $query * A search query string, that can contain options. * @param $type * The type of search, this maps to {search_index}.type. + * @param $lang + * Language the search terms are in, e.g. 'de' or 'en-US'; NULL if + * unknown/multiple. This is only used for language-specific preprocessing + * of keywords in the search query. * @return * The SearchQuery object. */ - public function searchExpression($expression, $type) { + public function searchExpression($expression, $type, $lang = NULL) { $this->searchExpression = $expression; $this->type = $type; + $this->term_lang = $lang; return $this; } @@ -188,8 +203,9 @@ $phrase = TRUE; $this->simple = FALSE; } - // Simplify keyword according to indexing rules and external preprocessors. - $words = search_simplify($match[2]); + // Simplify keyword according to indexing rules and external + // preprocessors. + $words = search_simplify($match[2], $this->term_lang); // Re-explode in case simplification added more words, except when // matching a phrase. $words = $phrase ? array($words) : preg_split('/ /', $words, -1, PREG_SPLIT_NO_EMPTY); Index: modules/search/search.api.php =================================================================== RCS file: /cvs/drupal/drupal/modules/search/search.api.php,v retrieving revision 1.16 diff -u -r1.16 search.api.php --- modules/search/search.api.php 18 Sep 2009 00:12:47 -0000 1.16 +++ modules/search/search.api.php 5 Oct 2009 19:48:56 -0000 @@ -14,20 +14,16 @@ /** * Define a custom search routine. * - * This hook allows a module to perform searches on content it defines - * (custom node types, users, or comments, for example) when a site search - * is performed. - * - * Note that you can use form API to extend the search. You will need to use - * hook_form_alter() to add any additional required form elements. You can - * process their values on submission using a custom validation function. - * You will need to merge any custom search values into the search keys - * using a key:value syntax. This allows all search queries to have a clean - * and permanent URL. See node_form_search_form_alter() for an example. - * - * The example given here is for node.module, which uses the indexed search - * capabilities. To do this, node module also implements hook_update_index() - * which is used to create and maintain the index. + * This hook allows a module to tell search.module that it wishes to + * perform searches on content it defines (custom node types, users, or + * comments, for example) when a site search is performed. + * + * In order for the search to do anything, your module must also implement + * hook_search_execute(), which is called when someone requests a search + * on your module's type of content. If you want to have your content + * indexed in the standard search index, your module should also implement + * hook_update_index(). If your search type has settings, you can implement + * hook_search_admin() to add them to the search settings page. * * @return * Array with the optional keys 'title' for the tab title and 'path' for @@ -46,7 +42,7 @@ /** * Define access to a custom search routine. * - * This hook allows a module to deny access to a user to a search tab. + * This hook allows a module to define permissions for a search tab. * * @ingroup search */ @@ -55,9 +51,9 @@ } /** - * The search index is going to be rebuilt. + * Take action when the search index is going to be rebuilt. * - * Modules which use hook_update_index() should update their indexing + * Modules which use hook_update_index() should update their indexing * bookkeeping so that it starts from scratch the next time * hook_update_index() is called. * @@ -71,7 +67,7 @@ } /** - * Report the stutus of indexing. + * Report the status of indexing. * * @return * An associative array with the key-value pairs: @@ -87,10 +83,10 @@ } /** - * Add elements to the search administration form. + * Add elements to the search settings page. * * @return - * The form array for the Search settings page at admin/config/search/settings. + * Form array for the Search settings page at admin/config/search/settings. * * @ingroup search */ @@ -121,11 +117,22 @@ /** * Execute a search for a set of key words. * - * We call do_search() with the keys, the module name, and extra SQL fragments - * to use when searching. See hook_update_index() for more information. + * Use the database API with the SearchQuery extender to perform the + * search, if your module uses the built-in search index (see + * hook_update_index() for information on how to use the index). + * + * Note that you can use form API to extend the search. You will need to use + * hook_form_alter() to add any additional required form elements. You can + * process their values on submission using a custom validation function. + * You will need to merge any custom search values into the search keys + * using a key:value syntax. This allows all search queries to have a clean + * and permanent URL. See node_form_search_form_alter() for an example. * * @param $keys * The search keywords as entered by the user. + * @param $lang + * The language the keywords are presumed to be in, e.g. 'de' or 'en-US'; + * NULL if unknown/multiple. * * @return * An array of search results. To use the default search result @@ -141,14 +148,14 @@ * * @ingroup search */ -function hook_search_execute($keys = NULL) { +function hook_search_execute($keys = NULL, $lang = NULL) { // Build matching conditions $query = db_search()->extend('PagerDefault'); $query->join('node', 'n', 'n.nid = i.sid'); $query ->condition('n.status', 1) ->addTag('node_access') - ->searchExpression($keys, 'node'); + ->searchExpression($keys, 'node', $lang); // Insert special keywords. $query->setOption('type', 'n.type'); @@ -204,34 +211,47 @@ } /** - * Preprocess text for the search index. + * Preprocess text for search. * - * This hook is called both for text added to the search index, as well as + * This hook is called to preprocess both the text added to the search index and * the keywords users have submitted for searching. * - * This is required for example to allow Japanese or Chinese text to be - * searched. As these languages do not use spaces, it needs to be split into - * separate words before it can be indexed. There are various external - * libraries for this. + * Possible uses: + * - Adding spaces between words of Chinese or Japanese text. + * - Stemming words down to their root words to allow matches between, for + * instance, walk, walked, walking, and walks in searching. + * - Expanding abbreviations and acronymns that occur in text. * * @param $text - * The text to split. This is a single piece of plain-text that was - * extracted from between two HTML tags. Will not contain any HTML entities. + * The text to preprocess. This is a single piece of plain text extracted + * from between two HTML tags or from the search query. It will not contain + * any HTML entities or HTML tags. + * @param $lang + * Language the text is in, e.g. 'de' or 'en-US'; NULL if unknown/multiple. * @return - * The text after processing. + * The text after preprocessing. Note that if your module decides not to alter + * the text, it should return the original text. Also, after preprocessing, + * words in the text should be separated by a space. * * @ingroup search */ -function hook_search_preprocess($text) { - // Do processing on $text +function hook_search_preprocess($text, $lang = NULL) { + // Check the language + if (!is_null( $lang ) && $lang != 'en') { + // Not a language we want to preprocess + return $text; + } + + // Do some processing on $text + return $text; } /** - * Update Drupal's full-text index for this module. + * Update the search index for this module. * - * Modules can implement this hook if they want to use the full-text indexing - * mechanism in Drupal. + * Modules can implement this hook if they want to use the built-in full-text + * search index to perform searches. * * This hook is called every cron run if search.module is enabled. A module * should check which of its items were modified or added since the last @@ -276,7 +296,7 @@ } // Update index - search_index($node->nid, 'node', $text); + search_index($node->nid, 'node', $text, $node->language); } } /** Index: modules/user/user.module =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.module,v retrieving revision 1.1055 diff -u -r1.1055 user.module --- modules/user/user.module 2 Oct 2009 14:49:10 -0000 1.1055 +++ modules/user/user.module 5 Oct 2009 19:48:56 -0000 @@ -798,7 +798,7 @@ /** * Implement hook_search_execute(). */ -function user_search_execute($keys = NULL) { +function user_search_execute($keys = NULL, $lang = NULL) { $find = array(); // Replace wildcards with MySQL/PostgreSQL wildcards. $keys = preg_replace('!\*+!', '%', $keys); Index: modules/node/node.module =================================================================== RCS file: /cvs/drupal/drupal/modules/node/node.module,v retrieving revision 1.1139 diff -u -r1.1139 node.module --- modules/node/node.module 5 Oct 2009 01:18:25 -0000 1.1139 +++ modules/node/node.module 5 Oct 2009 19:48:56 -0000 @@ -1362,14 +1362,28 @@ /** * Implement hook_search_execute(). */ -function node_search_execute($keys = NULL) { +function node_search_execute($keys = NULL, $lang = NULL) { // Build matching conditions $query = db_select('search_index', 'i')->extend('SearchQuery')->extend('PagerDefault'); $query->join('node', 'n', 'n.nid = i.sid'); + + // Override the language passed in if the user specified language. + $tmp_lang = search_expression_extract($keys, 'language'); + if ($tmp_lang) { + if (strpos( $tmp_lang, ',') !== FALSE) { + // multiple selection + $lang = NULL; + } + else { + // single selection + $lang = $tmp_lang; + } + } + $query ->condition('n.status', 1) ->addTag('node_access') - ->searchExpression($keys, 'node'); + ->searchExpression($keys, 'node', $lang); // Insert special keywords. $query->setOption('type', 'n.type'); @@ -2028,7 +2042,7 @@ } // Update index - search_index($node->nid, 'node', $text); + search_index($node->nid, 'node', $text, $node->language); } /**