<?php
define('PREG_CLASS_NUMBERS', '\\x{30}-\\x{39}\\x{b2}\\x{b3}\\x{b9}\\x{bc}-\\x{be}\\x{660}-\\x{669}\\x{6f0}-\\x{6f9}' . '\\x{966}-\\x{96f}\\x{9e6}-\\x{9ef}\\x{9f4}-\\x{9f9}\\x{a66}-\\x{a6f}\\x{ae6}-\\x{aef}' . '\\x{b66}-\\x{b6f}\\x{be7}-\\x{bf2}\\x{c66}-\\x{c6f}\\x{ce6}-\\x{cef}\\x{d66}-\\x{d6f}' . '\\x{e50}-\\x{e59}\\x{ed0}-\\x{ed9}\\x{f20}-\\x{f33}\\x{1040}-\\x{1049}\\x{1369}-' . '\\x{137c}\\x{16ee}-\\x{16f0}\\x{17e0}-\\x{17e9}\\x{17f0}-\\x{17f9}\\x{1810}-\\x{1819}' . '\\x{1946}-\\x{194f}\\x{2070}\\x{2074}-\\x{2079}\\x{2080}-\\x{2089}\\x{2153}-\\x{2183}' . '\\x{2460}-\\x{249b}\\x{24ea}-\\x{24ff}\\x{2776}-\\x{2793}\\x{3007}\\x{3021}-\\x{3029}' . '\\x{3038}-\\x{303a}\\x{3192}-\\x{3195}\\x{3220}-\\x{3229}\\x{3251}-\\x{325f}\\x{3280}-' . '\\x{3289}\\x{32b1}-\\x{32bf}\\x{ff10}-\\x{ff19}');
define('PREG_CLASS_PUNCTUATION', '\\x{21}-\\x{23}\\x{25}-\\x{2a}\\x{2c}-\\x{2f}\\x{3a}\\x{3b}\\x{3f}\\x{40}\\x{5b}-\\x{5d}' . '\\x{5f}\\x{7b}\\x{7d}\\x{a1}\\x{ab}\\x{b7}\\x{bb}\\x{bf}\\x{37e}\\x{387}\\x{55a}-\\x{55f}' . '\\x{589}\\x{58a}\\x{5be}\\x{5c0}\\x{5c3}\\x{5f3}\\x{5f4}\\x{60c}\\x{60d}\\x{61b}\\x{61f}' . '\\x{66a}-\\x{66d}\\x{6d4}\\x{700}-\\x{70d}\\x{964}\\x{965}\\x{970}\\x{df4}\\x{e4f}' . '\\x{e5a}\\x{e5b}\\x{f04}-\\x{f12}\\x{f3a}-\\x{f3d}\\x{f85}\\x{104a}-\\x{104f}\\x{10fb}' . '\\x{1361}-\\x{1368}\\x{166d}\\x{166e}\\x{169b}\\x{169c}\\x{16eb}-\\x{16ed}\\x{1735}' . '\\x{1736}\\x{17d4}-\\x{17d6}\\x{17d8}-\\x{17da}\\x{1800}-\\x{180a}\\x{1944}\\x{1945}' . '\\x{2010}-\\x{2027}\\x{2030}-\\x{2043}\\x{2045}-\\x{2051}\\x{2053}\\x{2054}\\x{2057}' . '\\x{207d}\\x{207e}\\x{208d}\\x{208e}\\x{2329}\\x{232a}\\x{23b4}-\\x{23b6}\\x{2768}-' . '\\x{2775}\\x{27e6}-\\x{27eb}\\x{2983}-\\x{2998}\\x{29d8}-\\x{29db}\\x{29fc}\\x{29fd}' . '\\x{3001}-\\x{3003}\\x{3008}-\\x{3011}\\x{3014}-\\x{301f}\\x{3030}\\x{303d}\\x{30a0}' . '\\x{30fb}\\x{fd3e}\\x{fd3f}\\x{fe30}-\\x{fe52}\\x{fe54}-\\x{fe61}\\x{fe63}\\x{fe68}' . '\\x{fe6a}\\x{fe6b}\\x{ff01}-\\x{ff03}\\x{ff05}-\\x{ff0a}\\x{ff0c}-\\x{ff0f}\\x{ff1a}' . '\\x{ff1b}\\x{ff1f}\\x{ff20}\\x{ff3b}-\\x{ff3d}\\x{ff3f}\\x{ff5b}\\x{ff5d}\\x{ff5f}-' . '\\x{ff65}');
define('PREG_CLASS_CJK', '\\x{1100}-\\x{11FF}\\x{3040}-\\x{309F}\\x{30A1}-\\x{318E}' . '\\x{31A0}-\\x{31B7}\\x{31F0}-\\x{31FF}\\x{3400}-\\x{4DBF}\\x{4E00}-\\x{9FCF}' . '\\x{A000}-\\x{A48F}\\x{A4D0}-\\x{A4FD}\\x{A960}-\\x{A97F}\\x{AC00}-\\x{D7FF}' . '\\x{F900}-\\x{FAFF}\\x{FF21}-\\x{FF3A}\\x{FF41}-\\x{FF5A}\\x{FF66}-\\x{FFDC}' . '\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}');
function search_help($path, $arg) {
switch ($path) {
case 'admin/help#search':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('The Search module provides the ability to index and search for content by exact keywords, and for users by username or e-mail. For more information, see the online handbook entry for <a href="@search-module">Search module</a>.', array(
'@search-module' => 'http://drupal.org/documentation/modules/search/',
'@search' => url('search'),
)) . '</p>';
$output .= '<h3>' . t('Uses') . '</h3>';
$output .= '<dl>';
$output .= '<dt>' . t('Searching content and users') . '</dt>';
$output .= '<dd>' . t('Users with <em>Use search</em> permission can use the search block and <a href="@search">Search page</a>. Users with the <em>View published content</em> permission can search for content containing exact keywords. Users with the <em>View user profiles</em> permission can search for users containing the keyword anywhere in the user name, and users with the <em>Administer users</em> permission can search for users by email address. Additionally, users with <em>Use advanced search</em> permission can find content using more complex search methods and filtering by choosing the <em>Advanced search</em> option on the <a href="@search">Search page</a>.', array(
'@search' => url('search'),
)) . '</dd>';
$output .= '<dt>' . t('Indexing content with cron') . '</dt>';
$output .= '<dd>' . t('To provide keyword searching, the search engine maintains an index of words found in the content and its fields, along with text added to your content by other modules (such as comments from the core Comment module, and taxonomy terms from the core Taxonomy module). To build and maintain this index, a correctly configured <a href="@cron">cron maintenance task</a> is required. Users with <em>Administer search</em> permission can further configure the cron settings on the <a href="@searchsettings">Search settings page</a>.', array(
'@cron' => 'http://drupal.org/cron',
'@searchsettings' => url('admin/config/search/settings'),
)) . '</dd>';
$output .= '<dt>' . t('Content reindexing') . '</dt>';
$output .= '<dd>' . t('Content-related actions on your site (creating, editing, or deleting content and comments) automatically cause affected content items to be marked for indexing or reindexing at the next cron run. When content is marked for reindexing, the previous content remains in the index until cron runs, at which time it is replaced by the new content. Unlike content-related actions, actions related to the structure of your site do not cause affected content to be marked for reindexing. Examples of structure-related actions that affect content include deleting or editing taxonomy terms, enabling or disabling modules that add text to content (such as Taxonomy, Comment, and field-providing modules), and modifying the fields or display parameters of your content types. If you take one of these actions and you want to ensure that the search index is updated to reflect your changed site structure, you can mark all content for reindexing by clicking the "Re-index site" button on the <a href="@searchsettings">Search settings page</a>. If you have a lot of content on your site, it may take several cron runs for the content to be reindexed.', array(
'@searchsettings' => url('admin/config/search/settings'),
)) . '</dd>';
$output .= '<dt>' . t('Configuring search settings') . '</dt>';
$output .= '<dd>' . t('Indexing behavior can be adjusted using the <a href="@searchsettings">Search settings page</a>. Users with <em>Administer search</em> permission can control settings such as the <em>Number of items to index per cron run</em>, <em>Indexing settings</em> (word length), <em>Active search modules</em>, and <em>Content ranking</em>, which lets you adjust the priority in which indexed content is returned in results.', array(
'@searchsettings' => url('admin/config/search/settings'),
)) . '</dd>';
$output .= '<dt>' . t('Search block') . '</dt>';
$output .= '<dd>' . t('The Search module includes a default <em>Search form</em> block, which can be enabled and configured on the <a href="@blocks">Blocks administration page</a>. The block is available to users with the <em>Search content</em> permission.', array(
'@blocks' => url('admin/structure/block'),
)) . '</dd>';
$output .= '<dt>' . t('Extending Search module') . '</dt>';
$output .= '<dd>' . t('By default, the Search module only supports exact keyword matching in content searches. You can modify this behavior by installing a language-specific stemming module for your language (such as <a href="http://drupal.org/project/porterstemmer">Porter Stemmer</a> for American English), which allows words such as walk, walking, and walked to be matched in the Search module. Another approach is to use a third-party search technology with stemming or partial word matching features built in, such as <a href="http://drupal.org/project/apachesolr">Apache Solr</a> or <a href="http://drupal.org/project/sphinx">Sphinx</a>. These and other <a href="@contrib-search">search-related contributed modules</a> can be downloaded by visiting Drupal.org.', array(
'@contrib-search' => 'http://drupal.org/project/modules?filters=tid%3A105',
)) . '</dd>';
$output .= '</dl>';
return $output;
case 'admin/config/search/settings':
return '<p>' . t('The search engine maintains an index of words found in your site\'s content. To build and maintain this index, a correctly configured <a href="@cron">cron maintenance task</a> is required. Indexing behavior can be adjusted using the settings below.', array(
'@cron' => url('admin/reports/status'),
)) . '</p>';
case 'search#noresults':
return t('<ul>
<li>Check if your spelling is correct.</li>
<li>Remove quotes around phrases to search for each word individually. <em>bike shed</em> will often show more results than <em>"bike shed"</em>.</li>
<li>Consider loosening your query with <em>OR</em>. <em>bike OR shed</em> will often show more results than <em>bike shed</em>.</li>
</ul>');
}
}
function search_theme() {
return array(
'search_block_form' => array(
'render element' => 'form',
'template' => 'search-block-form',
),
'search_result' => array(
'variables' => array(
'result' => NULL,
'module' => NULL,
),
'file' => 'search.pages.inc',
'template' => 'search-result',
),
'search_results' => array(
'variables' => array(
'results' => NULL,
'module' => NULL,
),
'file' => 'search.pages.inc',
'template' => 'search-results',
),
);
}
function search_permission() {
return array(
'administer search' => array(
'title' => t('Administer search'),
),
'search content' => array(
'title' => t('Use search'),
),
'use advanced search' => array(
'title' => t('Use advanced search'),
),
);
}
function search_block_info() {
$blocks['form']['info'] = t('Search form');
$blocks['form']['cache'] = DRUPAL_NO_CACHE;
$blocks['form']['properties']['administrative'] = TRUE;
return $blocks;
}
function search_block_view($delta = '') {
if (user_access('search content')) {
$block['content'] = drupal_get_form('search_block_form');
return $block;
}
}
function search_menu() {
$items['search'] = array(
'title' => 'Search',
'page callback' => 'search_view',
'access callback' => 'search_is_active',
'type' => MENU_SUGGESTED_ITEM,
'file' => 'search.pages.inc',
);
$items['admin/config/search/settings'] = array(
'title' => 'Search settings',
'description' => 'Configure relevance settings for search and other indexing options.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'search_admin_settings',
),
'access arguments' => array(
'administer search',
),
'weight' => -10,
'file' => 'search.admin.inc',
);
$items['admin/config/search/settings/reindex'] = array(
'title' => 'Clear index',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'search_reindex_confirm',
),
'access arguments' => array(
'administer search',
),
'type' => MENU_VISIBLE_IN_BREADCRUMB,
'file' => 'search.admin.inc',
);
drupal_static_reset('search_get_info');
$default_info = search_get_default_module_info();
if ($default_info) {
foreach (search_get_info() as $module => $search_info) {
$path = 'search/' . $search_info['path'];
$items[$path] = array(
'title' => $search_info['title'],
'page callback' => 'search_view',
'page arguments' => array(
$module,
'',
),
'access callback' => '_search_menu_access',
'access arguments' => array(
$module,
),
'type' => MENU_LOCAL_TASK,
'file' => 'search.pages.inc',
'weight' => $module == $default_info['module'] ? -10 : 0,
);
$items["{$path}/%menu_tail"] = array(
'title' => $search_info['title'],
'load arguments' => array(
'%map',
'%index',
),
'page callback' => 'search_view',
'page arguments' => array(
$module,
2,
),
'access callback' => '_search_menu_access',
'access arguments' => array(
$module,
),
'type' => MENU_LOCAL_TASK,
'file' => 'search.pages.inc',
'weight' => 0,
'tab_root' => 'search/' . $default_info['path'] . '/%',
'tab_parent' => 'search/' . $default_info['path'],
);
}
}
return $items;
}
function search_is_active() {
return user_access('search content') && search_get_info();
}
function search_get_info($all = FALSE) {
$search_hooks =& drupal_static(__FUNCTION__);
if (!isset($search_hooks)) {
foreach (module_implements('search_info') as $module) {
$search_hooks[$module] = call_user_func($module . '_search_info');
$search_hooks[$module] += array(
'title' => $module,
'path' => $module,
);
$search_hooks[$module]['module'] = $module;
}
}
if ($all) {
return $search_hooks;
}
$active = variable_get('search_active_modules', array(
'node',
'user',
));
return array_intersect_key($search_hooks, array_flip($active));
}
function search_get_default_module_info() {
$info = search_get_info();
$default = variable_get('search_default_module', 'node');
if (isset($info[$default])) {
return $info[$default];
}
return reset($info);
}
function _search_menu_access($name) {
return user_access('search content') && (!function_exists($name . '_search_access') || module_invoke($name, 'search_access'));
}
function search_reindex($sid = NULL, $module = NULL, $reindex = FALSE) {
if ($module == NULL && $sid == NULL) {
module_invoke_all('search_reset');
}
else {
db_delete('search_dataset')
->condition('sid', $sid)
->condition('type', $module)
->execute();
db_delete('search_index')
->condition('sid', $sid)
->condition('type', $module)
->execute();
if (!$reindex) {
db_delete('search_node_links')
->condition('sid', $sid)
->condition('type', $module)
->execute();
}
}
}
function search_dirty($word = NULL) {
$dirty =& drupal_static(__FUNCTION__, array());
if ($word !== NULL) {
$dirty[$word] = TRUE;
}
else {
return $dirty;
}
}
function search_cron() {
drupal_register_shutdown_function('search_update_totals');
foreach (variable_get('search_active_modules', array(
'node',
'user',
)) as $module) {
module_invoke($module, 'update_index');
}
}
function search_update_totals() {
foreach (search_dirty() as $word => $dummy) {
$total = db_query("SELECT SUM(score) FROM {search_index} WHERE word = :word", array(
':word' => $word,
), array(
'target' => 'slave',
))
->fetchField();
$total = log10(1 + 1 / max(1, $total));
db_merge('search_total')
->key(array(
'word' => $word,
))
->fields(array(
'count' => $total,
))
->execute();
}
$result = db_query("SELECT t.word AS realword, i.word FROM {search_total} t LEFT JOIN {search_index} i ON t.word = i.word WHERE i.word IS NULL", array(), array(
'target' => 'slave',
));
$or = db_or();
foreach ($result as $word) {
$or
->condition('word', $word->realword);
}
if (count($or) > 0) {
db_delete('search_total')
->condition($or)
->execute();
}
}
function search_simplify($text) {
$text = decode_entities($text);
$text = drupal_strtolower($text);
search_invoke_preprocess($text);
if (variable_get('overlap_cjk', TRUE)) {
$text = preg_replace_callback('/[' . PREG_CLASS_CJK . ']+/u', 'search_expand_cjk', $text);
}
$text = preg_replace('/([' . PREG_CLASS_NUMBERS . ']+)[' . PREG_CLASS_PUNCTUATION . ']+(?=[' . PREG_CLASS_NUMBERS . '])/u', '\\1', $text);
$text = preg_replace('/[.-]{2,}/', ' ', $text);
$text = preg_replace('/[._-]+/', '', $text);
$text = preg_replace('/[' . PREG_CLASS_UNICODE_WORD_BOUNDARY . ']+/u', ' ', $text);
$words = explode(' ', $text);
array_walk($words, '_search_index_truncate');
$text = implode(' ', $words);
return $text;
}
function search_expand_cjk($matches) {
$min = variable_get('minimum_word_size', 3);
$str = $matches[0];
$length = drupal_strlen($str);
if ($length <= $min) {
return ' ' . $str . ' ';
}
$tokens = ' ';
$chars = array();
for ($i = 0; $i < $length; $i++) {
$current = drupal_substr($str, 0, 1);
$str = substr($str, strlen($current));
$chars[] = $current;
if ($i >= $min - 1) {
$tokens .= implode('', $chars) . ' ';
array_shift($chars);
}
}
return $tokens;
}
function search_index_split($text) {
$last =& drupal_static(__FUNCTION__);
$lastsplit =& drupal_static(__FUNCTION__ . ':lastsplit');
if ($last == $text) {
return $lastsplit;
}
$text = search_simplify($text);
$words = explode(' ', $text);
$last = $text;
$lastsplit = $words;
return $words;
}
function _search_index_truncate(&$text) {
if (is_numeric($text)) {
$text = ltrim($text, '0');
}
$text = truncate_utf8($text, 50);
}
function search_invoke_preprocess(&$text) {
foreach (module_implements('search_preprocess') as $module) {
$text = module_invoke($module, 'search_preprocess', $text);
}
}
function search_index($sid, $module, $text) {
$minimum_word_size = variable_get('minimum_word_size', 3);
global $base_url;
$node_regexp = '@href=[\'"]?(?:' . preg_quote($base_url, '@') . '/|' . preg_quote(base_path(), '@') . ')(?:\\?q=)?/?((?![a-z]+:)[^\'">]+)[\'">]@i';
$tags = variable_get('search_tag_weights', array(
'h1' => 25,
'h2' => 18,
'h3' => 15,
'h4' => 12,
'h5' => 9,
'h6' => 6,
'u' => 3,
'b' => 3,
'i' => 3,
'strong' => 3,
'em' => 3,
'a' => 10,
));
$text = str_replace(array(
'<',
'>',
), array(
' <',
'> ',
), $text);
$text = strip_tags($text, '<' . implode('><', array_keys($tags)) . '>');
$split = preg_split('/\\s*<([^>]+?)>\\s*/', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
$tag = FALSE;
$link = FALSE;
$score = 1;
$accum = ' ';
$tagstack = array();
$tagwords = 0;
$focus = 1;
$results = array(
0 => array(),
);
foreach ($split as $value) {
if ($tag) {
list($tagname) = explode(' ', $value, 2);
$tagname = drupal_strtolower($tagname);
if ($tagname[0] == '/') {
$tagname = substr($tagname, 1);
if (!count($tagstack) || $tagstack[0] != $tagname) {
$tagstack = array();
$score = 1;
}
else {
$score = max(1, $score - $tags[array_shift($tagstack)]);
}
if ($tagname == 'a') {
$link = FALSE;
}
}
else {
if (isset($tagstack[0]) && $tagstack[0] == $tagname) {
$tagstack = array();
$score = 1;
}
else {
array_unshift($tagstack, $tagname);
$score += $tags[$tagname];
}
if ($tagname == 'a') {
if (preg_match($node_regexp, $value, $match)) {
$path = drupal_get_normal_path($match[1]);
if (preg_match('!(?:node|book)/(?:view/)?([0-9]+)!i', $path, $match)) {
$linknid = $match[1];
if ($linknid > 0) {
$node = db_query('SELECT title, nid, vid FROM {node} WHERE nid = :nid', array(
':nid' => $linknid,
), array(
'target' => 'slave',
))
->fetchObject();
$link = TRUE;
$linktitle = $node->title;
}
}
}
}
}
$tagwords = 0;
}
else {
if ($value != '') {
if ($link) {
if (preg_match('!^https?://!i', $value)) {
$value = $linktitle;
}
}
$words = search_index_split($value);
foreach ($words as $word) {
$accum .= $word . ' ';
if (is_numeric($word) || drupal_strlen($word) >= $minimum_word_size) {
if ($link) {
if (!isset($results[$linknid])) {
$results[$linknid] = array();
}
$results[$linknid][] = $word;
$focus *= 0.2;
}
if (!isset($results[0][$word])) {
$results[0][$word] = 0;
}
$results[0][$word] += $score * $focus;
$focus = min(1, 0.01 + 3.5 / (2 + count($results[0]) * 0.015));
}
$tagwords++;
if (count($tagstack) && $tagwords >= 15) {
$tagstack = array();
$score = 1;
}
}
}
}
$tag = !$tag;
}
search_reindex($sid, $module, TRUE);
db_insert('search_dataset')
->fields(array(
'sid' => $sid,
'type' => $module,
'data' => $accum,
'reindex' => 0,
))
->execute();
foreach ($results[0] as $word => $score) {
db_merge('search_index')
->key(array(
'word' => $word,
'sid' => $sid,
'type' => $module,
))
->fields(array(
'score' => $score,
))
->expression('score', 'score + :score', array(
':score' => $score,
))
->execute();
search_dirty($word);
}
unset($results[0]);
$result = db_query("SELECT nid, caption FROM {search_node_links} WHERE sid = :sid AND type = :type", array(
':sid' => $sid,
':type' => $module,
), array(
'target' => 'slave',
));
$links = array();
foreach ($result as $link) {
$links[$link->nid] = $link->caption;
}
foreach ($results as $nid => $words) {
$caption = implode(' ', $words);
if (isset($links[$nid])) {
if ($links[$nid] != $caption) {
db_update('search_node_links')
->fields(array(
'caption' => $caption,
))
->condition('sid', $sid)
->condition('type', $module)
->condition('nid', $nid)
->execute();
search_touch_node($nid);
}
unset($links[$nid]);
}
elseif ($sid != $nid || $module != 'node') {
db_insert('search_node_links')
->fields(array(
'caption' => $caption,
'sid' => $sid,
'type' => $module,
'nid' => $nid,
))
->execute();
search_touch_node($nid);
}
}
foreach ($links as $nid => $caption) {
db_delete('search_node_links')
->condition('sid', $sid)
->condition('type', $module)
->condition('nid', $nid)
->execute();
search_touch_node($nid);
}
}
function search_touch_node($nid) {
db_update('search_dataset')
->fields(array(
'reindex' => REQUEST_TIME,
))
->condition('type', 'node')
->condition('sid', $nid)
->execute();
}
function search_node_update_index($node) {
$result = db_query("SELECT caption FROM {search_node_links} WHERE nid = :nid", array(
':nid' => $node->nid,
), array(
'target' => 'slave',
));
$output = array();
foreach ($result as $link) {
$output[] = $link->caption;
}
if (count($output)) {
return '<a>(' . implode(', ', $output) . ')</a>';
}
}
function search_node_update($node) {
search_touch_node($node->nid);
}
function search_comment_insert($comment) {
search_touch_node($comment->nid);
}
function search_comment_update($comment) {
search_touch_node($comment->nid);
}
function search_comment_delete($comment) {
search_touch_node($comment->nid);
}
function search_comment_publish($comment) {
search_touch_node($comment->nid);
}
function search_comment_unpublish($comment) {
search_touch_node($comment->nid);
}
function search_expression_extract($expression, $option) {
if (preg_match('/(^| )' . $option . ':([^ ]*)( |$)/i', $expression, $matches)) {
return $matches[2];
}
}
function search_expression_insert($expression, $option, $value = NULL) {
$expression = trim(preg_replace('/(^| )' . $option . ':[^ ]*/i', '', $expression));
if (isset($value)) {
$expression .= ' ' . $option . ':' . trim($value);
}
return $expression;
}
function search_form($form, &$form_state, $action = '', $keys = '', $module = NULL, $prompt = NULL) {
$module_info = FALSE;
if (!$module) {
$module_info = search_get_default_module_info();
}
else {
$info = search_get_info();
$module_info = isset($info[$module]) ? $info[$module] : FALSE;
}
if (!$module_info) {
form_set_error(NULL, t('Search is currently disabled.'), 'error');
return $form;
}
if (!$action) {
$action = 'search/' . $module_info['path'];
}
if (!isset($prompt)) {
$prompt = t('Enter your keywords');
}
$form['#action'] = url($action);
$form_state['action'] = $action;
$form['#attributes']['class'][] = 'search-form';
$form['module'] = array(
'#type' => 'value',
'#value' => $module,
);
$form['basic'] = array(
'#type' => 'container',
'#attributes' => array(
'class' => array(
'container-inline',
),
),
);
$form['basic']['keys'] = array(
'#type' => 'textfield',
'#title' => $prompt,
'#default_value' => $keys,
'#size' => $prompt ? 40 : 20,
'#maxlength' => 255,
);
$form['basic']['processed_keys'] = array(
'#type' => 'value',
'#value' => '',
);
$form['basic']['submit'] = array(
'#type' => 'submit',
'#value' => t('Search'),
);
return $form;
}
function search_box($form, &$form_state, $form_id) {
$form[$form_id] = array(
'#type' => 'textfield',
'#title' => t('Search'),
'#title_display' => 'invisible',
'#size' => 15,
'#default_value' => '',
'#attributes' => array(
'title' => t('Enter the terms you wish to search for.'),
),
);
$form['actions'] = array(
'#type' => 'actions',
);
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Search'),
);
$form['#submit'][] = 'search_box_form_submit';
return $form;
}
function search_box_form_submit($form, &$form_state) {
if (isset($_GET['destination'])) {
unset($_GET['destination']);
}
if ($form_state['values']['search_block_form'] == '') {
form_set_error('keys', t('Please enter some keywords.'));
}
$form_id = $form['form_id']['#value'];
$info = search_get_default_module_info();
if ($info) {
$form_state['redirect'] = 'search/' . $info['path'] . '/' . trim($form_state['values'][$form_id]);
}
else {
form_set_error(NULL, t('Search is currently disabled.'), 'error');
}
}
function template_preprocess_search_block_form(&$variables) {
$variables['search'] = array();
$hidden = array();
foreach (element_children($variables['form']) as $key) {
$type = isset($variables['form'][$key]['#type']) ? $variables['form'][$key]['#type'] : '';
if ($type == 'hidden' || $type == 'token') {
$hidden[] = drupal_render($variables['form'][$key]);
}
else {
$variables['search'][$key] = drupal_render($variables['form'][$key]);
}
}
$variables['search']['hidden'] = implode($hidden);
$variables['search_form'] = implode($variables['search']);
}
function search_data($keys, $module, $conditions = NULL) {
if (module_hook($module, 'search_execute')) {
$results = module_invoke($module, 'search_execute', $keys, $conditions);
if (module_hook($module, 'search_page')) {
return module_invoke($module, 'search_page', $results);
}
else {
return array(
'#theme' => 'search_results',
'#results' => $results,
'#module' => $module,
);
}
}
}
function search_excerpt($keys, $text) {
$boundary = '(?:(?<=[' . PREG_CLASS_UNICODE_WORD_BOUNDARY . PREG_CLASS_CJK . '])|(?=[' . PREG_CLASS_UNICODE_WORD_BOUNDARY . PREG_CLASS_CJK . ']))';
preg_match_all('/ ("([^"]+)"|(?!OR)([^" ]+))/', ' ' . $keys, $matches);
$keys = array_merge($matches[2], $matches[3]);
$text = strip_tags(str_replace(array(
'<',
'>',
), array(
' <',
'> ',
), $text));
$text = decode_entities($text);
array_walk($keys, '_search_excerpt_replace');
$workkeys = $keys;
$ranges = array();
$included = array();
$foundkeys = array();
$length = 0;
while ($length < 256 && count($workkeys)) {
foreach ($workkeys as $k => $key) {
if (strlen($key) == 0) {
unset($workkeys[$k]);
unset($keys[$k]);
continue;
}
if ($length >= 256) {
break;
}
if (!isset($included[$key])) {
$included[$key] = 0;
}
$p = 0;
if (preg_match('/' . $boundary . $key . $boundary . '/iu', $text, $match, PREG_OFFSET_CAPTURE, $included[$key])) {
$p = $match[0][1];
}
else {
$info = search_simplify_excerpt_match($key, $text, $included[$key], $boundary);
if ($info['where']) {
$p = $info['where'];
if ($info['keyword']) {
$foundkeys[] = $info['keyword'];
}
}
}
if ($p) {
if (($q = strpos(' ' . $text, ' ', max(0, $p - 61))) !== FALSE) {
$end = substr($text . ' ', $p, 80);
if (($s = strrpos($end, ' ')) !== FALSE) {
$q = max($q - 1, 0);
$s = min($s, strlen($end) - 1);
$ranges[$q] = $p + $s;
$length += $p + $s - $q;
$included[$key] = $p + 1;
}
else {
unset($workkeys[$k]);
}
}
else {
unset($workkeys[$k]);
}
}
else {
unset($workkeys[$k]);
}
}
}
if (count($ranges) == 0) {
return check_plain(truncate_utf8($text, 256, TRUE, TRUE));
}
ksort($ranges);
$newranges = array();
foreach ($ranges as $from2 => $to2) {
if (!isset($from1)) {
$from1 = $from2;
$to1 = $to2;
continue;
}
if ($from2 <= $to1) {
$to1 = max($to1, $to2);
}
else {
$newranges[$from1] = $to1;
$from1 = $from2;
$to1 = $to2;
}
}
$newranges[$from1] = $to1;
$out = array();
foreach ($newranges as $from => $to) {
$out[] = substr($text, $from, $to - $from);
}
$dots = explode('!excerpt', t('... !excerpt ... !excerpt ...'));
$text = (isset($newranges[0]) ? '' : $dots[0]) . implode($dots[1], $out) . $dots[2];
$text = check_plain($text);
array_walk($foundkeys, '_search_excerpt_replace');
$keys = array_merge($keys, $foundkeys);
$text = preg_replace('/' . $boundary . '(' . implode('|', $keys) . ')' . $boundary . '/iu', '<strong>\\0</strong>', $text);
return $text;
}
function _search_excerpt_replace(&$text) {
$text = preg_quote($text, '/');
}
function search_simplify_excerpt_match($key, $text, $offset, $boundary) {
$pos = NULL;
$simplified_key = search_simplify($key);
$simplified_text = search_simplify($text);
if (!$simplified_key || !$simplified_text) {
return FALSE;
}
if (!preg_match('/' . $boundary . $simplified_key . $boundary . '/iu', $simplified_text, $match, PREG_OFFSET_CAPTURE, $offset)) {
return FALSE;
}
$split = array_filter(preg_split('/' . $boundary . '/iu', $text, -1, PREG_SPLIT_OFFSET_CAPTURE), '_search_excerpt_match_filter');
foreach ($split as $value) {
if ($value[1] < $offset) {
continue;
}
$window = substr($text, $value[1], 80);
$simplified_window = search_simplify($window);
if (strpos($simplified_window, $simplified_key) === 0) {
$pos = $value[1];
$length = strlen($window);
for ($i = 1; $i <= $length; $i++) {
$keyfound = substr($text, $value[1], $i);
if ($simplified_key == search_simplify($keyfound)) {
break;
}
}
break;
}
}
return $pos ? array(
'where' => $pos,
'keyword' => $keyfound,
) : FALSE;
}
function _search_excerpt_match_filter($var) {
return strlen(trim($var[0]));
}
function search_forms() {
$forms['search_block_form'] = array(
'callback' => 'search_box',
'callback arguments' => array(
'search_block_form',
),
);
return $forms;
}