diff options
-rw-r--r-- | mod/search/index.php | 182 | ||||
-rw-r--r-- | mod/search/languages/en.php | 22 | ||||
-rw-r--r-- | mod/search/search_hooks.php | 40 | ||||
-rw-r--r-- | mod/search/start.php | 480 | ||||
-rw-r--r-- | mod/search/views/default/page_elements/searchbox.php | 14 | ||||
-rw-r--r-- | mod/search/views/default/search/comments/listing.php | 70 | ||||
-rw-r--r-- | mod/search/views/default/search/css.php | 3 | ||||
-rw-r--r-- | mod/search/views/default/search/listing.php | 55 |
8 files changed, 351 insertions, 515 deletions
diff --git a/mod/search/index.php b/mod/search/index.php index ee6da32a8..e7081ecb5 100644 --- a/mod/search/index.php +++ b/mod/search/index.php @@ -27,11 +27,9 @@ $params = array( // 'tag_type' => $tag_type, 'owner_guid' => $owner_guid, // 'friends' => $friends + 'pagination' => ($search_type == 'all') ? FALSE : TRUE ); -$results_html = ''; -//$results_html .= elgg_view_title(elgg_echo('search:results')) . "<input type=\"text\" value=\"$query\" />"; -$results_html .= elgg_view_title(elgg_echo('search:results')); $types = get_registered_entity_types(); $custom_types = trigger_plugin_hook('search_types', 'get_types', $params, array()); @@ -105,13 +103,18 @@ foreach ($custom_types as $type) { // check that we have an actual query if (!$query) { - $body .= "No query."; + $body = elgg_view_title(elgg_echo('search:search_error')); + $body .= elgg_view('page_elements/contentwrapper', array('body' => elgg_echo('search:no_query'))); + $layout = elgg_view_layout('two_column_left_sidebar', '', $body); page_draw($title, $layout); return; } +// start the actual search +$results_html = ''; + if ($search_type == 'all' || $search_type == 'entities') { // to pass the correct search type to the views $params['search_type'] = 'entities'; @@ -199,177 +202,18 @@ if ($search_type != 'entities' || $search_type == 'all') { } } -//if ($search_type !== 'all') { -// var_dump('here'); -// $entities = trigger_plugin_hook('search', $search_type, '', $return); -//} -/* +// highlight search terms +$searched_words = search_remove_ignored_words($query, 'array'); +$highlighted_query = search_highlight_words($searched_words, $query); -call search_section_start to display long bar with types and titles -call search - -*/ +$body = elgg_view_title(sprintf(elgg_echo('search:results'), "\"$highlighted_query\"")); if (!$results_html) { - $body = elgg_echo('search:no_results'); + $body .= elgg_view('page_elements/contentwrapper', array('body' => elgg_echo('search:no_results'))); } else { - $body = $results_html; + $body .= $results_html; } $layout = elgg_view_layout('two_column_left_sidebar', '', $body); page_draw($title, $layout); - - - - - - - -return; - - -/** Main search page */ - -global $CONFIG; - -$tag = get_input('tag'); -$offset = get_input('offset', 0); -$viewtype = get_input('search_viewtype','list'); -if ($viewtype == 'gallery') { - $limit = get_input('limit', 12); // 10 items in list view -} else { - $limit = get_input('limit', 10); // 12 items in gallery view -} -$searchtype = get_input('searchtype', 'all'); -$type = get_input('type', ''); -$subtype = get_input('subtype', ''); -$owner_guid = get_input('owner_guid', ''); -$tagtype = get_input('tagtype', ''); -$friends = (int)get_input('friends', 0); -$title = sprintf(elgg_echo('searchtitle'), $tag); - -if (substr_count($owner_guid, ',')) { - $owner_guid_array = explode(',', $owner_guid); -} else { - $owner_guid_array = $owner_guid; -} -if ($friends > 0) { - if ($friends = get_user_friends($friends, '', 9999)) { - $owner_guid_array = array(); - foreach($friends as $friend) { - $owner_guid_array[] = $friend->guid; - } - } else { - $owner_guid = -1; - } -} - -// Set up submenus -if ($types = get_registered_entity_types()) { - foreach($types as $ot => $subtype_array) { - if (is_array($subtype_array) && count($subtype_array)) { - foreach($subtype_array as $object_subtype) { - $label = 'item:' . $ot; - if (!empty($object_subtype)) { - $label .= ':' . $object_subtype; - } - - $data = http_build_query(array( - 'tag' => urlencode($tag), - 'subtype' => $object_subtype, - 'type' => urlencode($ot), - //'tagtype' => urlencode($md_type), - 'owner_guid' => urlencode($owner_guid) - )); - - $url = "{$CONFIG->wwwroot}pg/search/?$data"; - - add_submenu_item(elgg_echo($label), $url); - } - } - } - - $data = http_build_query(array( - 'tag' => urlencode($tag), - 'owner_guid' => urlencode($owner_guid) - )); - - add_submenu_item(elgg_echo('all'), "{$CONFIG->wwwroot}pg/search/?$data"); -} - -// pull in search types for external or aggregated searches. -if ($search_types = trigger_plugin_hook('search', 'types', '', NULL, array())) { - -} - -$body = ''; -if (!empty($tag)) { - // start with blank results. - $results = array( - 'entities' => array(), - 'total' => 0 - ); - - // do the actual searchts - $params = array( - 'tag' => $tag, - 'offset' => $offset, - 'limit' => $limit, - 'searchtype' => $searchtype, - 'type' => $type, - 'subtype' => $subtype, - 'tagtype' => $tagtype, - 'owner_guid' => $owner_guid_array - ); - - $results = trigger_plugin_hook('search', 'entities', $params, $results); - - if (empty($type) && empty($subtype)) { - $title = sprintf(elgg_echo('searchtitle'),$tag); - } else { - if (empty($type)) { - $type = 'object'; - } - $itemtitle = 'item:' . $type; - if (!empty($subtype)) { - $itemtitle .= ':' . $subtype; - } - $itemtitle = elgg_echo($itemtitle); - $title = sprintf(elgg_echo('advancedsearchtitle'),$itemtitle,$tag); - } - - $body .= elgg_view_title($title); // elgg_view_title(sprintf(elgg_echo('searchtitle'),$tag)); - - // call the old (now-deprecated) search hook here - $body .= trigger_plugin_hook('search','',$tag, ''); - - $body .= elgg_view('search/startblurb', array('query' => $query)); - - if ($results->total > 0) { - $body .= elgg_view('search/entity_list', array( - 'entities' => $results->entities, - 'count' => $results->total, - 'offset' => $offset, - 'limit' => $limit, - 'baseurl' => $_SERVER['REQUEST_URI'], - 'fullview' => false, - 'context' => 'search', - 'viewtypetoggle' => true, - 'viewtype' => $viewtype, - 'pagination' => true - )); - } else { - $body .= elgg_view('page_elements/contentwrapper', array('body' => elgg_echo('search:noresults'))); - } - - elgg_view_entity_list($results->entities, count($results->entities), 0, count($results->entities), false); -} else { - // if no tag was given, give the user a box to input a search term - $body .= elgg_view_title(elgg_echo('search:enterterm')); - $body .= elgg_view('page_elements/contentwrapper', array('body' => '<div>' . elgg_view('page_elements/searchbox') . '</div>')); -} - -$layout = elgg_view_layout('two_column_left_sidebar','',$body); - -page_draw($title, $layout);
\ No newline at end of file diff --git a/mod/search/languages/en.php b/mod/search/languages/en.php index 52c29c7ed..f601256d0 100644 --- a/mod/search/languages/en.php +++ b/mod/search/languages/en.php @@ -1,10 +1,20 @@ <?php - $language_array = array('search:enterterm' => 'Enter a search term:', - 'search:noresults' => 'No results.', - 'search:matched' => 'Matched: ' - ); +$language_array = array( + 'search:enter_term' => 'Enter a search term:', + 'search:no_results' => 'No results.', + 'search:matched' => 'Matched: ', + 'search:results' => 'Results for %s', + 'search:no_query' => 'Please enter a query to search.', + 'search:search_error' => 'Error', -add_translation('en', $language_array); + 'search:more' => '+%s more %s', + + 'search_types:tags' => 'Tags', -?> + 'search_types:comments' => 'Comments', + 'search:comment_on' => 'Comment on "%s"', + 'search:unavailable_entity' => 'Unavailable Entity', +); + +add_translation('en', $language_array); diff --git a/mod/search/search_hooks.php b/mod/search/search_hooks.php index f7a49400f..8f0a62703 100644 --- a/mod/search/search_hooks.php +++ b/mod/search/search_hooks.php @@ -229,7 +229,10 @@ function search_comments_hook($hook, $type, $value, $params) { ); $fields = array('string'); - $search_where = search_get_where_sql('msv', $fields, $params); + + // force IN BOOLEAN MODE since fulltext isn't + // available on metastrings (and boolean mode doesn't need it) + $search_where = search_get_where_sql('msv', $fields, $params, FALSE); $e_access = get_access_sql_suffix('e'); $a_access = get_access_sql_suffix('a'); @@ -245,17 +248,44 @@ function search_comments_hook($hook, $type, $value, $params) { LIMIT {$params['offset']}, {$params['limit']} "; + $comments = get_data($q); +//elgg_get_entities() + $q = "SELECT count(DISTINCT a.id) as total FROM {$CONFIG->dbprefix}annotations a + JOIN {$CONFIG->dbprefix}metastrings msn ON a.name_id = msn.id + JOIN {$CONFIG->dbprefix}metastrings msv ON a.value_id = msv.id + JOIN {$CONFIG->dbprefix}entities e ON a.entity_guid = e.guid + WHERE msn.string IN ('generic_comment', 'group_topic_post') + AND ($search_where) + AND $e_access + AND $a_access + "; + + $result = get_data($q); + $count = $result[0]->total; + // @todo if plugins are disabled causing subtypes + // to be invalid and there are comments on entities of those subtypes, + // the counts will be wrong here and results might not show up correctly, + // especially on the search landing page, which only pulls out two results. + + // probably better to check against valid subtypes than to do what I'm doing. + // need to return actual entities // add the volatile data for why these entities have been returned. $entities = array(); foreach ($comments as $comment) { - $tags = implode(',', $entity->tags); - if (!$entity = get_entity($comment->entity_guid)) { - continue; + $entity = get_entity($comment->entity_guid); + + // hic sunt dracones + if (!$entity) { + //continue; + $entity = new ElggObject(); + $entity->setVolatileData('search_unavailable_entity', TRUE); } + $comment_str = search_get_highlighted_relevant_substrings($comment->comment, $query); + $entity->setVolatileData('search_match_annotation_id', $comment->id); $entity->setVolatileData('search_matched_comment', $comment_str); $entity->setVolatileData('search_matched_comment_owner_guid', $comment->owner_guid); $entity->setVolatileData('search_matched_comment_time_created', $comment->time_created); @@ -264,7 +294,7 @@ function search_comments_hook($hook, $type, $value, $params) { return array( 'entities' => $entities, - 'count' => count($entities), + 'count' => $count, ); } diff --git a/mod/search/start.php b/mod/search/start.php index 18b743cde..42366318a 100644 --- a/mod/search/start.php +++ b/mod/search/start.php @@ -40,7 +40,7 @@ function search_init() { // can't use get_data() here because some servers don't have these globals set, // which throws a db exception. $r = mysql_query('SELECT @@ft_min_word_len as min, @@ft_max_word_len as max'); - if ($word_lens = mysql_fetch_assoc($r)) { + if ($r && ($word_lens = mysql_fetch_assoc($r))) { $CONFIG->search_info['min_chars'] = $word_lens['min']; $CONFIG->search_info['max_chars'] = $word_lens['max']; } else { @@ -74,11 +74,8 @@ function search_page_handler($page) { } /** - * Return a string with highlighted matched elements. - * Checks for "s - * Provides context for matched elements. - * Will not return more than $max_length of full context. - * Only highlights words + * Return a string with highlighted matched queries and relevant context + * Determins context based upon occurance and distance of words with each other. * * @param unknown_type $haystack * @param unknown_type $need @@ -86,358 +83,235 @@ function search_page_handler($page) { * @param unknown_type $max_length * @return unknown_type */ -function search_get_highlighted_relevant_substrings($haystack, $needle, $min_match_context = 15, $max_length = 500) { +function search_get_highlighted_relevant_substrings($haystack, $query, $min_match_context = 30, $max_length = 300) { global $CONFIG; $haystack = strip_tags($haystack); - $haystack_lc = strtolower($haystack); -// -// $haystack = "Like merge sort, quicksort can also be easily parallelized due to its " -// . "divide-and-conquer nature. Individual in-place partition operations are difficult " -// . "to parallelize, but once divided, different sections of the list can be sorted in parallel. " -// . "If we have p processors, we can divide a list of n ele"; -// -// $needle = 'difficult to sort in parallel'; - - // for now don't worry about "s or boolean operators - $needle = str_replace(array('"', '-', '+', '~'), '', stripslashes(strip_tags($needle))); - $words = explode(' ', $needle); + $haystack_length = elgg_strlen($haystack); + $haystack_lc = elgg_strtolower($haystack); - $min_chars = $CONFIG->search_info['min_chars']; - // if > ft_min_word == not running in literal mode. - if ($needle >= $min_chars) { - // clean out any words that are ignored by mysql - foreach ($words as $i => $word) { - if (strlen($word) < $min_chars) { - unset ($words[$i]); - } - } - } + $words = search_remove_ignored_words($query, 'array'); - /* + // if haystack < $max_length return the entire haystack w/formatting immediately + if ($haystack_length <= $max_length) { + $return = search_highlight_words($words, $haystack); - $body_len = 250 - - $context = 5-30, 20-45, 75-100, 150 - - can pull out context either on: - one of each matching term - X # of highest matching terms + return $return; + } - */ - $substr_counts = array(); - $str_pos = array(); - // matrices for being and end context lengths. - // defaults to min context. will add additional context later if needed + // get the starting positions and lengths for all matching words $starts = array(); - $stops = array(); - - // map the words to the starts and stops - $words_arg = array(); - $context_count = 0; - - - // get the full count of matches. + $lengths = array(); foreach ($words as $word) { - $word = strtolower($word); - $count = substr_count($haystack, $word); - $word_len = strlen($word); + $word = elgg_strtolower($word); + $count = elgg_substr_count($haystack_lc, $word); + $word_len = elgg_strlen($word); // find the start positions for the words if ($count > 1) { - $str_pos[$word] = array(); $offset = 0; - while (FALSE !== $pos = strpos($haystack, $word, $offset)) { - $str_pos[$word][] = $pos; - $starts[] = ($pos - $min_match_context > 0) ? $pos - $min_match_context : 0; - $stops[] = $pos + $word_len + $min_match_context; - $words_arg[] = $word; - $context_count += $min_match_context + $word_len; + while (FALSE !== $pos = elgg_strpos($haystack_lc, $word, $offset)) { + $start = ($pos - $min_match_context > 0) ? $pos - $min_match_context : 0; + $starts[] = $start; + $stop = $pos + $word_len + $min_match_context; + $lengths[] = $stop - $start; $offset += $pos + $word_len; } } else { - $pos = strpos($haystack, $word); - $str_pos[$word] = array($pos); - $starts[] = ($pos - $min_match_context > 0) ? $pos - $min_match_context : 0; - $stops[] = $pos + $word_len + $min_match_context; - $context_count += $min_match_context + $word_len; - $words_arg[] = $word; - } - $substr_counts[$word] = $count; - } - - // sort by order of occurence - //krsort($substr_counts); - $full_count = array_sum($substr_counts); - - // figure out what the context needs to be. - // take one of each matched phrase - // if there are any - -// -// var_dump($str_pos); -// var_dump($substr_counts); -// var_dump($context_count); - - - // sort to put them in order of occurence - asort($starts, SORT_NUMERIC); - asort($stops, SORT_NUMERIC); - - // offset them correctly - $starts[] = 0; - $new_stops = array(0); - foreach ($stops as $i => $pos) { - $new_stops[$i+1] = $pos; - } - $stops = $new_stops; - - $substrings = array(); - $len = count($starts); - - $starts = array_merge($starts); - $stops = array_merge($stops); - - $offsets = array(); - $limits = array(); - $c = 0; - foreach ($starts as $i => $start) { - $stop = $stops[$i]; - $offsets[$c] = $start; - $limits[$c] = $stop; - - // never need the last one as it's just a displacing entry - if ($c+1 == count($starts)) { - break; + $pos = elgg_strpos($haystack_lc, $word); + $start = ($pos - $min_match_context > 0) ? $pos - $min_match_context : 0; + $starts[] = $start; + $stop = $pos + $word_len + $min_match_context; + $lengths[] = $stop - $start; } - - if ($start - $stop < 0) { - //var_dump("Looking at c=$c & $start - $stop and going to unset {$limits[$c]}"); - unset($offsets[$c]); - unset($limits[$c]); - } - $c++; } - // reset indexes and remove placeholder elements. - $limits = array_merge($limits); - array_shift($limits); - $offsets = array_merge($offsets); - array_pop($offsets); + $offsets = search_consolidate_substrings($starts, $lengths); - // figure out if we need to adjust the offsets from the base - // this could result in overlapping summaries. - // might be nicer to just remove it. - - $total_len = 0; - foreach ($offsets as $i => $offset) { - $total_len += $limits[$i] - $offset; - } + // figure out if we can adjust the offsets and lengths + // in order to return more context + $total_length = array_sum($offsets); $add_length = 0; if ($total_length < $max_length) { - $add_length = floor((($max_length - $total_len) / count($offsets)) / 2); - } - - $lengths = array(); - foreach ($offsets as $i => $offset) { - $limit = $limits[$i]; - if ($offset == 0 && $add_length) { - $limit += $add_length; - } else { - $offset = $offset - $add_length; + $add_length = floor((($max_length - $total_length) / count($offsets)) / 2); + + $starts = array(); + $lengths = array(); + foreach ($offsets as $offset => $length) { + $start = ($offset - $add_length > 0) ? $offset - $add_length : 0; + $length = $length + $add_length; + $starts[] = $start; + $lengths[] = $length; } - $string = substr($haystack, $offset, $limit - $offset); - if ($offset != 0) { - $string = "...$string"; - } - - if ($limit + $offset >= strlen($haystack)) { - $string .= '...'; - } - - $substrings[] = $string; - $lengths[] = strlen($string); + $offsets = search_consolidate_substrings($starts, $lengths); } - // sort by length of context. - asort($lengths); + // sort by order of string size descending (which is roughly + // the proximity of matched terms) so we can keep the + // substrings with terms closest together and discard + // the others as needed to fit within $max_length. + arsort($offsets); - $matched = ''; - foreach ($lengths as $i => $len) { - $string = $substrings[$i]; + $return_strs = array(); + $total_length = 0; + foreach ($offsets as $start => $length) { + $string = trim(elgg_substr($haystack, $start, $length)); - if (strlen($matched) + strlen($string) < $max_length) { - $matched .= $string; + // continue past if adding this substring exceeds max length + if ($total_length + $length > $max_length) { + continue; } - } - $i = 1; - foreach ($words as $word) { - $search = "/($word)/i"; - $replace = "<strong class=\"searchMatch searchMatchColor$i\">$1</strong>"; - $matched = preg_replace($search, $replace, $matched); - $i++; + $total_length += $length; + $return_strs[$start] = $string; } - return $matched; - - - // crap below.. - - - - for ($i=0; $i<$len; $i++) { - $start = $starts[$i]; - $stop = $stops[$i]; - var_dump("Looking at $i = $start - $stop"); - - while ($start - $stop <= 0) { - $stop = $stops[$i++]; - var_dump("New start is $stop"); - } + // put the strings in order of occurence + ksort($return_strs); - var_dump("$start-$stop"); + // add ...s where needed + $return = implode('...', $return_strs); + if (!array_key_exists(0, $return_strs)) { + $return = "...$return"; } - // find the intersecting contexts - foreach ($starts as $i => $start_pos) { - $words .= "{$words_arg[$i]}\t\t\t"; - echo "$start_pos\t\t\t"; + // add to end of string if last substring doesn't hit the end. + $starts = array_keys($return_strs); + $last_pos = $starts[count($starts)-1]; + if ($last_pos + elgg_strlen($return_strs[$last_pos]) < $haystack_length) { + $return .= '...'; } - echo "\n"; + $return = search_highlight_words($words, $return); - foreach ($stops as $i => $stop_pos) { - echo "$stop_pos\t\t\t"; - } -echo "\n$words\n"; - - // get full number of matches against all words to see how many we actually want to look at. - - - - -// $desc = search_get_relevant_substring($entity->description, $params['query'], '<strong class="searchMatch">', '</strong>'); - - - $params['query']; - // "this is"just a test "silly person" + return $return; +} - // check for "s - $words_quotes = explode('"', $needle); - $words_orig = explode(' ', $needle); - $words = array(); +/** + * Takes an array of offsets and lengths and consolidates any + * overlapping entries, returning an array of new offsets and lengths + * + * Offsets and lengths are specified in separate arrays because of possible + * index collisions with the offsets. + * + * @param array $offsets + * @param array $lengths + * @return array + */ +function search_consolidate_substrings($offsets, $lengths) { + // sort offsets by occurence + asort($offsets, SORT_NUMERIC); - foreach ($words_orig as $i => $word) { - // figure out if we have a special operand - $operand = substr($word, 0, 1); - switch($operand) { - case '"': - // find the matching " if any. else, remove the " - if (substr_count($query, '"') < 2) { - $words[] = substr($word, 1); - } else { - $word = substr($word, 1); - $word_i = $i; - while ('"' != strpos($words_orig[$word_i], '"')) { - $word .= " {$words_orig[$word_i]}"; - unset($words_orig[$word_i]); - } - } + // reset the indexes maintaining association with the original offsets. + $offsets = array_merge($offsets); - break; + $new_lengths = array(); + foreach ($offsets as $i => $offset) { + $new_lengths[] = $lengths[$i]; + } - case '+': - // remove + - $words[] = substr($word, 1); - break; + $lengths = $new_lengths; - case '~': - case '-': - // remove this from highlighted list. + $return = array(); + $count = count($offsets); + for ($i=0; $i<$count; $i++) { + $offset = $offsets[$i]; + $length = $lengths[$i]; + $end_pos = $offset + $length; + // find the next entry that doesn't overlap + while(array_key_exists($i+1, $offsets) && $end_pos > $offsets[$i+1]) { + $i++; + if (!array_key_exists($i, $offsets)) { break; + } + $end_pos = $lengths[$i] + $offsets[$i]; } - } - // pick out " queries - if (substr_count($query, '"') >= 2) { + $length = $end_pos - $offset; + // will never have a colliding offset, so can return as a single array + $return[$offset] = $length; } - // ignore queries starting with - - - - // @todo figure out a way to "center" the matches within the max_length. - // if only one match, its context is $context + $max_length / 2 - // if 2 matches, its context is $context + $max_length / 4 - // if 3 matches, its context is $context + $max_length / 6 - // $context per match = $min_match_context + ($max_length / $num_count_match) - - // if $max_length / ($matched_count * 2) < $context - // only match against the first X matches where $context >= $context + return $return; } /** - * Returns a matching string with $context amount of context, optionally - * surrounded by $before and $after. - * - * If no match is found, restricts string to $context*2 starting from strpos 0. + * Safely highlights the words in $words found in $string avoiding recursion * - * @param str $haystack - * @param str $needle - * @param str $before - * @param str $after - * @param int $context - * @return str + * @param array $words + * @param string $string + * @return string */ -function search_get_relevant_substring($haystack, $needle, $before = '', $after = '', $context = 75) { - $haystack = strip_tags($haystack); - $needle = strip_tags($needle); - - $pos = strpos(strtolower($haystack), strtolower($needle)); +function search_highlight_words($words, $string) { + $i = 1; + $replace_html = array( + 'strong' => rand(10000,99999), + 'class' => rand(10000,99999), + 'searchMatch' => rand(10000,99999), + 'searchMatchColor' => rand(10000,99999) + ); - if ($pos === FALSE) { - $str = substr($haystack, 0, $context*2); - if (strlen($haystack) > $context*2) { - $str .= '...'; - } + foreach ($words as $word) { + $search = "/($word)/i"; - return $str; + // must replace with placeholders in case one of the search terms is + // in the html string. + // later, will replace the placeholders with the actual html. + // Yeah this is hacky. I'm tired. + $strong = $replace_html['strong']; + $class = $replace_html['class']; + $searchMatch = $replace_html['searchMatch']; + $searchMatchColor = $replace_html['searchMatchColor']; + + $replace = "<$strong $class=\"$searchMatch $searchMatchColor{$i}\">$1</$strong>"; + $string = preg_replace($search, $replace, $string); + $i++; } - $start_pos = $pos - $context; - - if ($start_pos < 0) { - $start_pos = 0; + foreach ($replace_html as $replace => $search) { + $string = str_replace($search, $replace, $string); } - // get string from -context to +context - $matched = substr($haystack, $start_pos, $context*2); + return $string; +} - // add elipses to front. - if ($start_pos > 0) { - $matched = "...$matched"; - } +/** + * Returns a query with stop and too short words removed. + * (Unless the entire query is < ft_min_word_chars, in which case + * it's taken literally.) + * + * @param array $query + * @param str $format Return as an array or a string + * @return mixed + */ +function search_remove_ignored_words($query, $format = 'array') { + global $CONFIG; + + // don't worry about "s or boolean operators + $query = str_replace(array('"', '-', '+', '~'), '', stripslashes(strip_tags($query))); + $words = explode(' ', $query); - // add elipses to end. - if ($pos + strlen($needle) + $context*2 < strlen($haystack)) { - $matched = "$matched..."; + $min_chars = $CONFIG->search_info['min_chars']; + // if > ft_min_word we're not running in literal mode. + if ($query >= $min_chars) { + // clean out any words that are ignored by mysql + foreach ($words as $i => $word) { + if (elgg_strlen($word) < $min_chars) { + unset ($words[$i]); + } + } } - // surround if needed - // @todo would getting each position of the match then - // inserting manually based on the position be faster than preg_replace()? - if ($before || $after) { - $matched = str_ireplace($needle, $before . $needle . $after, $matched); - //$matched = mb_ereg_replace("") - // insert before + if ($format == 'string') { + return implode(' ', $words); } - return $matched; + return $words; } @@ -498,7 +372,7 @@ function search_get_listing_html($entities, $count, $params) { * @param array $params Original search params * @return str */ -function search_get_where_sql($table, $fields, $params) { +function search_get_where_sql($table, $fields, $params, $use_fulltext = TRUE) { global $CONFIG; $query = $params['query']; @@ -507,49 +381,49 @@ function search_get_where_sql($table, $fields, $params) { $fields[$i] = "$table.$field"; } + // if we're not using full text, rewrite the query for bool mode. + // exploiting a feature(ish) of bool mode where +-word is the same as -word + if (!$use_fulltext) { + $query = '+' . str_replace(' ', ' +', $query); + } + // if query is shorter than the min for fts words // it's likely a single acronym or similar // switch to literal mode - if (strlen($query) < $CONFIG->search_info['min_chars']) { + if (elgg_strlen($query) < $CONFIG->search_info['min_chars']) { $likes = array(); $query = sanitise_string($query); foreach ($fields as $field) { $likes[] = "$field LIKE '%$query%'"; } $likes_str = implode(' OR ', $likes); - //$where = "($table.guid = e.guid AND ($likes_str))"; $where = "($likes_str)"; } else { // if using advanced or paired "s, switch into boolean mode - if ((isset($params['advanced_search']) && $params['advanced_search']) || substr_count($query, '"') >= 2 ) { + if (!$use_fulltext + || (isset($params['advanced_search']) && $params['advanced_search']) + || elgg_substr_count($query, '"') >= 2 ) { $options = 'IN BOOLEAN MODE'; } else { - // natural language mode is default and this keyword isn't supported - // in < 5.1 + // natural language mode is default and this keyword isn't supported in < 5.1 //$options = 'IN NATURAL LANGUAGE MODE'; $options = ''; } // if short query, use query expansion. - if (strlen($query) < 6) { + // @todo doesn't seem to be working well. + if (elgg_strlen($query) < 5) { //$options .= ' WITH QUERY EXPANSION'; } $query = sanitise_string($query); - // if query is shorter than the ft_min_word_len switch to literal mode. $fields_str = implode(',', $fields); - //$where = "($table.guid = e.guid AND (MATCH ($fields_str) AGAINST ('$query' $options)))"; $where = "(MATCH ($fields_str) AGAINST ('$query' $options))"; } return $where; } -function search_get_query_where_sql($table, $query) { - // if there are multiple "s or 's it's a literal string. - -} - /** Register init system event **/ register_elgg_event_handler('init','system','search_init');
\ No newline at end of file diff --git a/mod/search/views/default/page_elements/searchbox.php b/mod/search/views/default/page_elements/searchbox.php index cfc0b953b..f7746b0da 100644 --- a/mod/search/views/default/page_elements/searchbox.php +++ b/mod/search/views/default/page_elements/searchbox.php @@ -1,4 +1,16 @@ +<?php + +if (array_key_exists('value', $vars)) { + $value = $vars['value']; +} elseif ($value = get_input('q', get_input('tag', NULL))) { + $value = $value; +} else { + $value = elgg_echo('search'); +} + +?> + <form id="searchform" action="<?php echo $vars['url']; ?>pg/search/" method="get"> - <input type="text" size="21" name="q" value="<?php echo elgg_echo('search'); ?>" onclick="if (this.value=='<?php echo elgg_echo('search'); ?>') { this.value='' }" class="search_input" /> + <input type="text" size="21" name="q" value="<?php echo $value; ?>" onclick="if (this.value=='<?php echo elgg_echo('search'); ?>') { this.value='' }" class="search_input" /> <input type="submit" value="<?php echo elgg_echo('search:go'); ?>" class="search_submit_button" /> </form> diff --git a/mod/search/views/default/search/comments/listing.php b/mod/search/views/default/search/comments/listing.php index 58353a110..13f368f2b 100644 --- a/mod/search/views/default/search/comments/listing.php +++ b/mod/search/views/default/search/comments/listing.php @@ -7,16 +7,12 @@ * @author Curverider Ltd * @link http://elgg.org/ */ -?> -<div class="search_listing"> -<?php if (!is_array($vars['entities']) || !count($vars['entities'])) { return FALSE; } $title_str = elgg_echo('comments'); -$body = elgg_view_title($title_str); $query = htmlspecialchars(http_build_query( array( @@ -30,9 +26,33 @@ $query = htmlspecialchars(http_build_query( )); $url = "{$vars['url']}pg/search?$query"; -$more = "<a href=\"$url\">+$count more $title_str</a>"; -echo elgg_view('page_elements/contentwrapper', array('body' => $body)); +// get pagination +if (array_key_exists('pagination', $vars) && $vars['pagination']) { + $nav .= elgg_view('navigation/pagination',array( + 'baseurl' => $url, + 'offset' => $vars['params']['offset'], + 'count' => $vars['count'], + 'limit' => $vars['params']['limit'], + )); +} else { + $nav = ''; +} + +// get more links +$more_check = $vars['count'] - ($vars['params']['offset'] + $vars['params']['limit']); +$more = ($more_check > 0) ? $more_check : 0; + +if ($more) { + $title_key = ($more == 1) ? 'comment' : 'comments'; + $more_str = sprintf(elgg_echo('search:more'), $vars['count'], elgg_echo($title_key)); + $more_link = "<a href=\"$url\">$more_str</a>"; +} else { + $more_link = ''; +} + +echo $nav; +$body = elgg_view_title($title_str); foreach ($vars['entities'] as $entity) { if ($owner = $entity->getOwnerEntity()) { @@ -41,22 +61,46 @@ foreach ($vars['entities'] as $entity) { } else { $icon = ''; } - $title = "Comment on " . elgg_echo('item:' . $entity->getType() . ':' . $entity->getSubtype()); + + // @todo Sometimes we find comments on entities we can't display... + if ($entity->getVolatileData('search_unavailable_entity')) { + $title = sprintf(elgg_echo('search:comment_on'), elgg_echo('search:unavailable_entity')); + // keep anchor for formatting. + $title = "<a>$title</a>"; + } else { + if ($entity->getType() == 'object') { + $title = $entity->title; + } else { + $title = $entity->name; + } + + if (!$title) { + $title = elgg_echo('item:' . $entity->getType() . ':' . $entity->getSubtype()); + } + + if (!$title) { + $title = elgg_echo('item:' . $entity->getType()); + } + + $title = sprintf(elgg_echo('search:comment_on'), $title); + $url = $entity->getURL() . '#annotation-' . $entity->getVolatileData('search_match_annotation_id'); + $title = "<a href=\"$url\">$title</a>"; + } + $description = $entity->getVolatileData('search_matched_comment'); - $url = $entity->getURL(); - $title = "<a href=\"$url\">$title</a>"; $tc = $entity->getVolatileData('search_matched_comment_time_created');; $time = friendly_time($tc); - echo <<<___END + $body .= <<<___END <span class="searchListing"> <h3 class="searchTitle">$title</h3> <span class="searchDetails"> <span class="searchDescription">$description</span><br /> - $icon $time - $more</a> + $icon $time - $more_link</a> </span> </span> ___END; } -?> -</div> + +$body .= $nav; +echo elgg_view('page_elements/contentwrapper', array('body' => $body)); diff --git a/mod/search/views/default/search/css.php b/mod/search/views/default/search/css.php index 28dc82a4a..19cca02e8 100644 --- a/mod/search/views/default/search/css.php +++ b/mod/search/views/default/search/css.php @@ -38,7 +38,6 @@ margin: 6px; background-color: #99FF99; } - .searchTitle { text-decoration: underline; } @@ -91,7 +90,7 @@ margin: 6px; } /* override the entity container piece */ -.search_listing .entity_listing { +.search_listing .search_listing { -webkit-border-radius: 0px; -moz-border-radius: 0px; background: transparent; diff --git a/mod/search/views/default/search/listing.php b/mod/search/views/default/search/listing.php index 37850c911..f947bd808 100644 --- a/mod/search/views/default/search/listing.php +++ b/mod/search/views/default/search/listing.php @@ -7,11 +7,8 @@ * @author Curverider Ltd * @link http://elgg.org/ */ -?> -<div class="search_listing"> -<?php $entities = $vars['entities']; $count = $vars['count'] - count($vars['entities']); @@ -19,24 +16,52 @@ if (!is_array($vars['entities']) || !count($vars['entities'])) { return FALSE; } -$title_str = elgg_echo("item:{$vars['params']['type']}:{$vars['params']['subtype']}"); -$body = elgg_view_title($title_str); +// figure out what we're deal with. +if (array_key_exists('type', $vars['params']) && array_key_exists('subtype', $vars['params'])) { + $type_str = elgg_echo("item:{$vars['params']['type']}:{$vars['params']['subtype']}"); +} elseif (array_key_exists('type', $vars['params'])) { + $type_str = elgg_echo("item:{$vars['params']['type']}"); +} else { + $type_str = elgg_echo('search:unknown_entity'); +} $query = htmlspecialchars(http_build_query( array( 'q' => $vars['params']['query'], 'entity_type' => $vars['params']['type'], 'entity_subtype' => $vars['params']['subtype'], - 'limit' => get_input('limit', 10), - 'offset' => get_input('offset', 0), 'search_type' => 'entities', ) )); $url = "{$vars['url']}pg/search?$query"; -$more = "<a href=\"$url\">+$count more $title_str</a>"; -echo elgg_view('page_elements/contentwrapper', array('body' => $body)); +// get pagination +if (array_key_exists('pagination', $vars['params']) && $vars['params']['pagination']) { + $nav .= elgg_view('navigation/pagination',array( + 'baseurl' => $url, + 'offset' => $vars['params']['offset'], + 'count' => $vars['count'], + 'limit' => $vars['params']['limit'], + )); +} else { + $nav = ''; +} + +// get any more links. +$more_check = $vars['count'] - ($vars['params']['offset'] + $vars['params']['limit']); +$more = ($more_check > 0) ? $more_check : 0; + +if ($more) { + $title_key = ($more == 1) ? 'comment' : 'comments'; + $more_str = sprintf(elgg_echo('search:more'), $count, $type_str); + $more_link = "<a href=\"$url\">$more_str</a>"; +} else { + $more_link = ''; +} + +echo $nav; +$body = elgg_view_title($title_str); foreach ($entities as $entity) { if ($owner = $entity->getOwnerEntity()) { @@ -53,16 +78,14 @@ foreach ($entities as $entity) { $tu = $entity->time_updated; $time = friendly_time(($tu > $tc) ? $tu : $tc); - echo <<<___END + $body .= <<<___END <span class="searchListing"> <h3 class="searchTitle">$title</h3> - <span class="searchDetails"> - <span class="searchDescription">$description</span><br /> - $icon $time - $more</a> - </span> + <span class="searchDescription">$description</span><br /> + <span class="searchInfo">$icon $time - $more_link</span> </span> ___END; } -?> -</div>
\ No newline at end of file +echo elgg_view('page_elements/contentwrapper', array('body' => $body)); +echo $nav;
\ No newline at end of file |