diff --git a/modules/search.module b/modules/search.module
index 4fafed50a97..002ea09c26c 100644
--- a/modules/search.module
+++ b/modules/search.module
@@ -69,6 +69,22 @@ function search_perm() {
return array('search content', 'administer search');
}
+/**
+ * Implementation of hook_block().
+ */
+function search_block($op = 'list', $delta = 0) {
+ global $user;
+ if ($op == 'list') {
+ $blocks[0]['info'] = t('Search form');
+ return $blocks;
+ }
+ else if ($op == 'view' && user_access('search content') && arg(0) != 'search') {
+ $block['content'] = search_form('', '', null, '');
+ $block['subject'] = t('Search');
+ return $block;
+ }
+}
+
/**
* Implementation of hook_menu().
*/
@@ -81,24 +97,30 @@ function search_menu($may_cache) {
'access' => user_access('search content'),
'type' => MENU_SUGGESTED_ITEM);
- foreach (module_list() as $name) {
- if (module_hook($name, 'search')) {
- $items[] = array('path' => 'search/'. $name, 'title' => module_invoke($name, 'search', 'name'),
- 'callback' => 'search_view',
- 'access' => user_access('search content'),
- 'type' => $name == 'node' ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK);
- }
- }
-
$items[] = array('path' => 'admin/settings/search', 'title' => t('search'),
'callback' => 'search_admin',
'type' => MENU_NORMAL_ITEM,
'access' => user_access('administer site configuration'));
}
+ else if (arg(0) == 'search') {
+ // To remember the user's search keywords when switching across tabs,
+ // we dynamically add the keywords to the search tabs' paths.
+ $keys = search_get_keys();
+ $keys = strlen($keys) ? '/'. $keys : '';
+ foreach (module_list() as $name) {
+ if (module_hook($name, 'search')) {
+ $items[] = array('path' => 'search/'. $name . $keys, 'title' => module_invoke($name, 'search', 'name'),
+ 'callback' => 'search_view',
+ 'access' => user_access('search content'),
+ 'type' => MENU_LOCAL_TASK);
+ }
+ }
+ }
return $items;
}
+
/**
* Menu callback; displays the search module settings page.
*/
@@ -371,6 +393,7 @@ function search_index($sid, $type, $text) {
foreach ($words as $word) {
// Check wordlength
if (string_length($word) >= $minimum_word_size) {
+ // Note: strtolower can be used because the value is only used internally.
$word = strtolower($word);
if ($link) {
if (!isset($results[$linknid])) {
@@ -443,32 +466,36 @@ function search_index($sid, $type, $text) {
*/
function do_search($keys, $type, $join = '', $where = '1') {
// Note, we replace the wildcards with U+FFFD (Replacement character) to pass
- // through the keyword extractor.
- $keys = str_replace('*', '�', $keys);
+ // through the keyword extractor. Multiple wildcards are collapsed into one.
+ $keys = preg_replace('!\*+!', '�', $keys);
// Split into words
$keys = search_keywords_split($keys);
- // Lowercase
- foreach ($keys as $k => $v) {
- $keys[$k] = strtolower($v);
- }
$words = array();
$arguments = array();
+ $refused = array();
// Build WHERE clause
foreach ($keys as $word) {
if (string_length($word) < variable_get('remove_short', 3)) {
+ if ($word != '') {
+ $refused[] = str_replace('�', '*', $word);
+ }
continue;
}
if (strpos($word, '�') !== false) {
+ // Note: strtolower can be used because the value is only used internally.
$words[] = "i.word LIKE '%s'";
- $arguments[] = str_replace('�', '%', $word);
+ $arguments[] = str_replace('�', '%', strtolower($word));
}
else {
$words[] = "i.word = '%s'";
- $arguments[] = $word;
+ $arguments[] = strtolower($word);
}
}
+ // Tell the user which words were excluded
+ drupal_set_message(t('The following word(s) were not included because they were too short: %words', array('%words' => ''. implode(', ', $refused) .'')));
+
if (count($words) == 0) {
return array();
}
@@ -493,12 +520,35 @@ function do_search($keys, $type, $join = '', $where = '1') {
return $results;
}
+/**
+ * Helper function for grabbing search keys.
+ */
+function search_get_keys() {
+ // Extract keys as remainder of path
+ // Note: support old GET format of searches for existing links.
+ $path = explode('/', $_GET['q'], 3);
+ return count($path) == 3 ? $path[2] : $_REQUEST['keys'];
+}
+
/**
* Menu callback; presents the search form and/or search results.
*/
function search_view() {
- $keys = isset($_GET['keys']) ? $_GET['keys'] : $_POST['edit']['keys'];
- $type = arg(1) ? arg(1) : (isset($_GET['type']) ? $_GET['type'] : ($_POST['edit']['type'] ? $_POST['edit']['type'] : 'node'));
+ $type = arg(1);
+
+ // 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
+ if ($_POST['edit']['keys']) {
+ drupal_goto('search/'. $type .'/'. urlencode($_POST['edit']['keys']));
+ }
+ else if ($type == '') {
+ // Note: search/node can not be a default tab because it would take on the
+ // path of its parent (search). It would prevent remembing keywords when
+ // switching tabs. This is why we drupal_goto to it from the parent instead.
+ drupal_goto('search/node');
+ }
+ $keys = search_get_keys();
if (user_access('search content')) {
// Only perform search if there is non-whitespace search term:
@@ -526,7 +576,7 @@ function search_view() {
// Construct the search form.
// Note, we do this last because of the form_set_error() above.
- $output = search_form(NULL, $keys, $type, TRUE);
+ $output = search_form(NULL, $keys, $type);
$output .= $results;
@@ -535,7 +585,6 @@ function search_view() {
else {
drupal_access_denied();
}
-
}
/**
@@ -576,10 +625,12 @@ function search_view() {
* @param $type
* The type of search to render the node for. Must be the name of module
* which implements hook_search(). Defaults to 'node'.
+ * @param $prompt
+ * A piece of text to put before the form (e.g. "Enter your keywords")
* @return
* An HTML string containing the search form.
*/
-function search_form($action = '', $keys = '', $type = null) {
+function search_form($action = '', $keys = '', $type = null, $prompt = null) {
$edit = $_POST['edit'];
if (!$action) {
@@ -588,14 +639,16 @@ function search_form($action = '', $keys = '', $type = null) {
if (!$type) {
$type = 'node';
}
+ if (is_null($prompt)) {
+ $prompt = t('Enter your keywords');
+ }
$output = '
';
return form($output, 'post', $action);
@@ -616,7 +669,7 @@ function search_data($keys = NULL, $type = 'node') {
$output .= theme('search_item', $entry, $type);
}
$output .= '';
- $output .= theme('pager', NULL, 15, 0, array('keys' => $keys, 'type' => $type));
+ $output .= theme('pager', NULL, 15, 0);
}
}
}
diff --git a/modules/search/search.module b/modules/search/search.module
index 4fafed50a97..002ea09c26c 100644
--- a/modules/search/search.module
+++ b/modules/search/search.module
@@ -69,6 +69,22 @@ function search_perm() {
return array('search content', 'administer search');
}
+/**
+ * Implementation of hook_block().
+ */
+function search_block($op = 'list', $delta = 0) {
+ global $user;
+ if ($op == 'list') {
+ $blocks[0]['info'] = t('Search form');
+ return $blocks;
+ }
+ else if ($op == 'view' && user_access('search content') && arg(0) != 'search') {
+ $block['content'] = search_form('', '', null, '');
+ $block['subject'] = t('Search');
+ return $block;
+ }
+}
+
/**
* Implementation of hook_menu().
*/
@@ -81,24 +97,30 @@ function search_menu($may_cache) {
'access' => user_access('search content'),
'type' => MENU_SUGGESTED_ITEM);
- foreach (module_list() as $name) {
- if (module_hook($name, 'search')) {
- $items[] = array('path' => 'search/'. $name, 'title' => module_invoke($name, 'search', 'name'),
- 'callback' => 'search_view',
- 'access' => user_access('search content'),
- 'type' => $name == 'node' ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK);
- }
- }
-
$items[] = array('path' => 'admin/settings/search', 'title' => t('search'),
'callback' => 'search_admin',
'type' => MENU_NORMAL_ITEM,
'access' => user_access('administer site configuration'));
}
+ else if (arg(0) == 'search') {
+ // To remember the user's search keywords when switching across tabs,
+ // we dynamically add the keywords to the search tabs' paths.
+ $keys = search_get_keys();
+ $keys = strlen($keys) ? '/'. $keys : '';
+ foreach (module_list() as $name) {
+ if (module_hook($name, 'search')) {
+ $items[] = array('path' => 'search/'. $name . $keys, 'title' => module_invoke($name, 'search', 'name'),
+ 'callback' => 'search_view',
+ 'access' => user_access('search content'),
+ 'type' => MENU_LOCAL_TASK);
+ }
+ }
+ }
return $items;
}
+
/**
* Menu callback; displays the search module settings page.
*/
@@ -371,6 +393,7 @@ function search_index($sid, $type, $text) {
foreach ($words as $word) {
// Check wordlength
if (string_length($word) >= $minimum_word_size) {
+ // Note: strtolower can be used because the value is only used internally.
$word = strtolower($word);
if ($link) {
if (!isset($results[$linknid])) {
@@ -443,32 +466,36 @@ function search_index($sid, $type, $text) {
*/
function do_search($keys, $type, $join = '', $where = '1') {
// Note, we replace the wildcards with U+FFFD (Replacement character) to pass
- // through the keyword extractor.
- $keys = str_replace('*', '�', $keys);
+ // through the keyword extractor. Multiple wildcards are collapsed into one.
+ $keys = preg_replace('!\*+!', '�', $keys);
// Split into words
$keys = search_keywords_split($keys);
- // Lowercase
- foreach ($keys as $k => $v) {
- $keys[$k] = strtolower($v);
- }
$words = array();
$arguments = array();
+ $refused = array();
// Build WHERE clause
foreach ($keys as $word) {
if (string_length($word) < variable_get('remove_short', 3)) {
+ if ($word != '') {
+ $refused[] = str_replace('�', '*', $word);
+ }
continue;
}
if (strpos($word, '�') !== false) {
+ // Note: strtolower can be used because the value is only used internally.
$words[] = "i.word LIKE '%s'";
- $arguments[] = str_replace('�', '%', $word);
+ $arguments[] = str_replace('�', '%', strtolower($word));
}
else {
$words[] = "i.word = '%s'";
- $arguments[] = $word;
+ $arguments[] = strtolower($word);
}
}
+ // Tell the user which words were excluded
+ drupal_set_message(t('The following word(s) were not included because they were too short: %words', array('%words' => ''. implode(', ', $refused) .'')));
+
if (count($words) == 0) {
return array();
}
@@ -493,12 +520,35 @@ function do_search($keys, $type, $join = '', $where = '1') {
return $results;
}
+/**
+ * Helper function for grabbing search keys.
+ */
+function search_get_keys() {
+ // Extract keys as remainder of path
+ // Note: support old GET format of searches for existing links.
+ $path = explode('/', $_GET['q'], 3);
+ return count($path) == 3 ? $path[2] : $_REQUEST['keys'];
+}
+
/**
* Menu callback; presents the search form and/or search results.
*/
function search_view() {
- $keys = isset($_GET['keys']) ? $_GET['keys'] : $_POST['edit']['keys'];
- $type = arg(1) ? arg(1) : (isset($_GET['type']) ? $_GET['type'] : ($_POST['edit']['type'] ? $_POST['edit']['type'] : 'node'));
+ $type = arg(1);
+
+ // 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
+ if ($_POST['edit']['keys']) {
+ drupal_goto('search/'. $type .'/'. urlencode($_POST['edit']['keys']));
+ }
+ else if ($type == '') {
+ // Note: search/node can not be a default tab because it would take on the
+ // path of its parent (search). It would prevent remembing keywords when
+ // switching tabs. This is why we drupal_goto to it from the parent instead.
+ drupal_goto('search/node');
+ }
+ $keys = search_get_keys();
if (user_access('search content')) {
// Only perform search if there is non-whitespace search term:
@@ -526,7 +576,7 @@ function search_view() {
// Construct the search form.
// Note, we do this last because of the form_set_error() above.
- $output = search_form(NULL, $keys, $type, TRUE);
+ $output = search_form(NULL, $keys, $type);
$output .= $results;
@@ -535,7 +585,6 @@ function search_view() {
else {
drupal_access_denied();
}
-
}
/**
@@ -576,10 +625,12 @@ function search_view() {
* @param $type
* The type of search to render the node for. Must be the name of module
* which implements hook_search(). Defaults to 'node'.
+ * @param $prompt
+ * A piece of text to put before the form (e.g. "Enter your keywords")
* @return
* An HTML string containing the search form.
*/
-function search_form($action = '', $keys = '', $type = null) {
+function search_form($action = '', $keys = '', $type = null, $prompt = null) {
$edit = $_POST['edit'];
if (!$action) {
@@ -588,14 +639,16 @@ function search_form($action = '', $keys = '', $type = null) {
if (!$type) {
$type = 'node';
}
+ if (is_null($prompt)) {
+ $prompt = t('Enter your keywords');
+ }
$output = ' ';
return form($output, 'post', $action);
@@ -616,7 +669,7 @@ function search_data($keys = NULL, $type = 'node') {
$output .= theme('search_item', $entry, $type);
}
$output .= '';
- $output .= theme('pager', NULL, 15, 0, array('keys' => $keys, 'type' => $type));
+ $output .= theme('pager', NULL, 15, 0);
}
}
}
diff --git a/modules/user.module b/modules/user.module
index 74e4350f86b..7681cdd8206 100644
--- a/modules/user.module
+++ b/modules/user.module
@@ -423,8 +423,8 @@ function user_search($op = 'search', $keys = null) {
case 'search':
$find = array();
// Replace wildcards with MySQL/PostgreSQL wildcards.
- $keys = str_replace('*', '%', $keys);
- $result = db_query_range("SELECT * FROM {users} WHERE LOWER(name) LIKE '%%%s%%'", strtolower($keys), 0, 20);
+ $keys = preg_replace('!\*+!', '%', $keys);
+ $result = pager_query("SELECT * 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/view"));
}
diff --git a/modules/user/user.module b/modules/user/user.module
index 74e4350f86b..7681cdd8206 100644
--- a/modules/user/user.module
+++ b/modules/user/user.module
@@ -423,8 +423,8 @@ function user_search($op = 'search', $keys = null) {
case 'search':
$find = array();
// Replace wildcards with MySQL/PostgreSQL wildcards.
- $keys = str_replace('*', '%', $keys);
- $result = db_query_range("SELECT * FROM {users} WHERE LOWER(name) LIKE '%%%s%%'", strtolower($keys), 0, 20);
+ $keys = preg_replace('!\*+!', '%', $keys);
+ $result = pager_query("SELECT * 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/view"));
}