1 | <?php |
---|
2 | /** |
---|
3 | * WordPress API for searching. |
---|
4 | * |
---|
5 | * @package WordPress |
---|
6 | * @subpackage Search |
---|
7 | */ |
---|
8 | |
---|
9 | /** |
---|
10 | * Search API |
---|
11 | * An API that is loaded for search related actions |
---|
12 | * The class contains various functions for outputing to WordPress and doing the necessary legwork for common search functions |
---|
13 | * (pagination, trimming content for display, options and filtering, etc) |
---|
14 | */ |
---|
15 | class WP_Search |
---|
16 | { |
---|
17 | |
---|
18 | var $plugin = NULL; //Reference to the active search plugin |
---|
19 | var $flags = array(); // Array of search parameters |
---|
20 | var $custom_options = array(); // Options array to edit in the admin control panel |
---|
21 | |
---|
22 | /** |
---|
23 | * Constructor |
---|
24 | * This function creates a new filter 'search_load' so that search plugins can call it and load their class object into "$this->plugin". |
---|
25 | */ |
---|
26 | function WP_Search() { |
---|
27 | // Check if the plugin is loaded before we try to load this class to it |
---|
28 | if( $this->plugin == NULL ) { |
---|
29 | $this->plugin = apply_filters( 'search_load', array() ); |
---|
30 | $this->plugin->flags =& $this->create_flags(); |
---|
31 | $this->plugin->parent =& $this; |
---|
32 | } |
---|
33 | |
---|
34 | if( $this->plugin->options['advanced'] == 1 && $_GET['advancedsearch'] == "1" ) |
---|
35 | add_filter( 'template_redirect', array( &$this, 'advanced_search_wrapper' ) ); |
---|
36 | if( $this->plugin->options['advanced'] == 1 ) |
---|
37 | add_filter( 'get_search_form', array( &$this, 'advanced_link' ) ); |
---|
38 | |
---|
39 | $this->custom_options = unserialize( get_option( "searchapi_custom_options" ) ); |
---|
40 | |
---|
41 | /** |
---|
42 | * Config Warning |
---|
43 | * Shows a warning if you need to configure additonal settings |
---|
44 | */ |
---|
45 | function config_warning() { |
---|
46 | echo "<div id='message' class='updated fade'><p><strong>" . __('The search system is almost ready.' ) . "</strong> ".sprintf( __ ( '<a href="%1$s">You must fill out a few additional configuration fields before the search engine can be used.</a>'), "options-general.php?page=search/search.php" ) . "</p></div>"; |
---|
47 | } |
---|
48 | |
---|
49 | // Should we show the above notice? |
---|
50 | if( is_array( $this->custom_options ) && ( count ( $this->custom_options ) > 0 ) ) { |
---|
51 | foreach( $this->custom_options as $setting ) { |
---|
52 | if( $setting['required'] == 1 ) { |
---|
53 | if( ! trim( get_option( $setting['id'] ) ) ) { |
---|
54 | add_action('admin_notices', 'config_warning'); |
---|
55 | break; |
---|
56 | } |
---|
57 | } |
---|
58 | } |
---|
59 | } |
---|
60 | } |
---|
61 | |
---|
62 | |
---|
63 | /** |
---|
64 | * Initialize Search |
---|
65 | * This function overloads the template outputer so that we can replace the content with our search results. We do this so the legacy search template or older search stuff |
---|
66 | * doesn't load instead. This function calls execute_search which does the bulk of the work for this class. |
---|
67 | * @see search_api::execute_search() |
---|
68 | */ |
---|
69 | function init_search() { |
---|
70 | add_filter( 'template_redirect', array( &$this, 'execute_search' ) ); |
---|
71 | } |
---|
72 | |
---|
73 | /** |
---|
74 | * Build Options |
---|
75 | * This function takes a list of custom options a plugin might have (a search api key, a charset string, etc) so that you can edit them from the control panel |
---|
76 | * @param array $options An array of options stored in arrays containing ids, values, titles and descriptions |
---|
77 | */ |
---|
78 | function build_options( $options, $help = '' ) { |
---|
79 | global $wpdb; |
---|
80 | |
---|
81 | foreach( $options as $option ) { |
---|
82 | add_option( esc_attr__ ( $option['id'] ) , esc_attr__ ( $option['value'] ) ); |
---|
83 | } |
---|
84 | |
---|
85 | update_option( "searchapi_custom_options", serialize( $options ) ); |
---|
86 | update_option( "searchapi_help", $wpdb->escape( $help ) ); |
---|
87 | } |
---|
88 | |
---|
89 | /** |
---|
90 | * Options Admin Menu |
---|
91 | * This function creates a menu under "settings" for search settings if the search plugin has any |
---|
92 | */ |
---|
93 | function options_admin_menu() { |
---|
94 | if( is_array ( $this->custom_options ) ) { |
---|
95 | add_options_page ( |
---|
96 | __("Search Settings"), |
---|
97 | __("Search Settings"), |
---|
98 | 'manage_options', |
---|
99 | __FILE__, |
---|
100 | array( &$this, "options_admin_page" ) |
---|
101 | ); |
---|
102 | } |
---|
103 | } |
---|
104 | |
---|
105 | /** |
---|
106 | * Options Admin Page |
---|
107 | * This function takes creates a page for editing search settings |
---|
108 | */ |
---|
109 | function options_admin_page() { |
---|
110 | global $wpdb; |
---|
111 | echo "<div class='wrap'><h2>" . __( "Search Settings " ) . "</h2>"; |
---|
112 | |
---|
113 | if( get_option( 'searchapi_help' ) ) |
---|
114 | echo "<div id='message' class='update fade'>\n" . stripslashes( get_option( 'searchapi_help' ) ) . "</div>\n"; |
---|
115 | |
---|
116 | // Update settings |
---|
117 | if( $_REQUEST['submit'] ) { |
---|
118 | $ok = false; |
---|
119 | |
---|
120 | foreach( $this->custom_options as $option ) { |
---|
121 | if( !empty( $_REQUEST[$option['id']] ) ) { |
---|
122 | update_option( $wpdb->escape( $option['id'] ), $wpdb->escape ( $_REQUEST[$option['id']] ) ); |
---|
123 | $ok = true; |
---|
124 | } |
---|
125 | } |
---|
126 | |
---|
127 | if( $ok == true ) |
---|
128 | echo "<div id='message' class='update fade'><p>" . __("Options Saved") . "</p></div>"; |
---|
129 | else |
---|
130 | echo "<div id='message' class='error fade'><p>" . __("Failed to save options.") . "</p></div>"; |
---|
131 | } |
---|
132 | |
---|
133 | // Display the form |
---|
134 | echo "<form method='post'>\n"; |
---|
135 | |
---|
136 | foreach( $this->custom_options as $option ) { |
---|
137 | echo "<p><label for='{$option['id']}'> " . esc_html__( $option['title'] ) . ": \n"; |
---|
138 | if( !empty( $option['desc'] ) ) |
---|
139 | echo "<br /> " . esc_html__( $option['desc'] ) . "<br />"; |
---|
140 | echo "<input type='text' name='{$option['id']}' value='" . esc_attr__( stripslashes( get_option( $option['id'] ) ) ) ."' /></label></p>"; |
---|
141 | } |
---|
142 | |
---|
143 | echo "<input type='submit' name='submit' class='button-primary' value='Save Settings' /></form>"; |
---|
144 | |
---|
145 | echo "</div>"; |
---|
146 | } |
---|
147 | |
---|
148 | /** |
---|
149 | * Pagination |
---|
150 | * This function takes the total number of results from a set and calculates how many pages is needed to display X (WordPress's posts per page option) results per page. |
---|
151 | * @param int $total The total number of results from a query |
---|
152 | * @return string HTML output containing the pagination links |
---|
153 | */ |
---|
154 | function pagination( $total ) { |
---|
155 | // Only go through the pagination code if the search plugin calls for it |
---|
156 | if( $this->plugin->options['pagination'] == 1 ) { |
---|
157 | |
---|
158 | // Load some required variables such as the total results, the total needed pages and variable place holders. |
---|
159 | if( $total > 0 ) |
---|
160 | $pages = ceil( $total / get_option( 'posts_per_page' ) ); |
---|
161 | |
---|
162 | if( empty ( $_GET['pg'] ) ) |
---|
163 | $_GET['pg'] = 1; |
---|
164 | |
---|
165 | $pages = $pages ? $pages : 1; |
---|
166 | $links = ""; |
---|
167 | |
---|
168 | // Grab the current URL (minus the query string) since search pages can have many query strings |
---|
169 | $current = "http" . ( empty( $_SERVER["HTTPS"] ) ? "": ( $_SERVER["HTTPS"]=='on' ) ? "s": "" )."://" . esc_attr__( $_SERVER["HTTP_HOST"] ) . esc_attr__( $_SERVER["REQUEST_URI"] ); |
---|
170 | $current = preg_replace( "/&pg=([0-9]+)/i", "", $current ); |
---|
171 | |
---|
172 | // Create the previous, next and number links |
---|
173 | if( $_GET['pg'] > 1 ) |
---|
174 | $previous_link = "<span class=\"searchpglink\"><a href=\"".$current."&pg=".($_GET['pg'] - 1)."\"><</a></span> "; |
---|
175 | |
---|
176 | if( $_GET['pg'] < $pages ) |
---|
177 | $next_link = " <span class=\"searchpglink\"><a href=\"".$current."&pg=".($_GET['pg'] + 1)."\">></a></span>"; |
---|
178 | |
---|
179 | if( $pages > 1 ) { |
---|
180 | for( $i = 0, $j = $pages - 1; $i <= $j; ++$i ) { |
---|
181 | $pagenum = $i+1; |
---|
182 | $page = ceil( $pagenum ); |
---|
183 | |
---|
184 | if ( $pagenum < ( $_GET['pg'] - 4 ) ) { |
---|
185 | $i = $_GET['pg'] - 6; |
---|
186 | continue; |
---|
187 | } |
---|
188 | |
---|
189 | if ( $page == $_GET['pg'] ) |
---|
190 | $links .= " <span class=\"searchpgcurrent\">{$page}</span>"; // this is the current page, no need for a link |
---|
191 | else { |
---|
192 | $links .= " <span class=\"searchpglink\"><a href=\"".$current."&pg=".$page."\" title=\"$page\">$page</a></span>"; |
---|
193 | if ( $pagenum > ( $_GET['pg'] + 4 ) ) |
---|
194 | break; |
---|
195 | } |
---|
196 | } |
---|
197 | } |
---|
198 | } |
---|
199 | |
---|
200 | // FILTER: search_pagination Allows you to edit the output of the pagination links |
---|
201 | return apply_filters('search_pagination', $previous_link . $links . $next_link); |
---|
202 | } |
---|
203 | |
---|
204 | /** |
---|
205 | * Advanced Search Wrapper |
---|
206 | * This function creates a "virtual" page which is used to display all the advanced search form HTML. The short code [advsearch] is replaced with output from |
---|
207 | * the advanced_search function here. |
---|
208 | * @see search_api::advanced_search() |
---|
209 | * @global class WordPress DB Abstraction Layer |
---|
210 | */ |
---|
211 | function advanced_search_wrapper() { |
---|
212 | global $wp_query; |
---|
213 | |
---|
214 | // set "the loop" to nothing |
---|
215 | $wp_query->posts = NULL; |
---|
216 | |
---|
217 | // Create a blank "result set" for the wordpress query object |
---|
218 | $object = new stdClass(); |
---|
219 | $object->post_title = __( "Advanced Search" ); |
---|
220 | $object->post_content = "[advsearch]"; |
---|
221 | |
---|
222 | $wp_query->posts[0] = $object; |
---|
223 | |
---|
224 | // replace the advsearch shortcode with the form, and then return the page template |
---|
225 | add_shortcode( 'advsearch', array( &$this, 'advanced_search' ) ); |
---|
226 | |
---|
227 | // Return the template |
---|
228 | $template = get_page_template(); |
---|
229 | |
---|
230 | if ( $template ) |
---|
231 | include $template; |
---|
232 | else |
---|
233 | include(TEMPLATEPATH . "/index.php"); |
---|
234 | |
---|
235 | exit; |
---|
236 | } |
---|
237 | |
---|
238 | /** |
---|
239 | * Advanced Link |
---|
240 | * This function returns a link to the advanced search form. If additonal output is passed to it then that is also returned. |
---|
241 | * @param string $output Output to be returned, usually passed from get_search_form or another WordPress search |
---|
242 | * @return string HTML output containing an advanced search link |
---|
243 | */ |
---|
244 | function advanced_link( $output = '' ) { |
---|
245 | // output usually comes from get_search-form |
---|
246 | if( !empty( $output ) ) |
---|
247 | $html = $output; |
---|
248 | |
---|
249 | // return a link to the advanced search form |
---|
250 | $html .= "<small><a href='index.php?advancedsearch=1'>". __( "Advanced Search" ) . "</a></small>"; |
---|
251 | |
---|
252 | // FILTER: search_advanced_link Allows you to edit the output of the advanced link and the search in the sidebar (using the filter get_search_template) |
---|
253 | return apply_filters('search_advanced_link', $html); |
---|
254 | } |
---|
255 | |
---|
256 | /** |
---|
257 | * Advanced Search |
---|
258 | * This function generates HTML for an avanced search form (author, categories, etc). This function is outputed using a WordPress short code. |
---|
259 | * @return string HTML output for an advanced search form |
---|
260 | */ |
---|
261 | function advanced_search() { |
---|
262 | |
---|
263 | // Content type picker |
---|
264 | $option_output = "<br /> " . __( "Search:" ) ." <input type='checkbox' name='types[]' value='posts' /> " . __( "Posts" ) ." <input type='checkbox' name='types[]' value='pages' /> " . __( "Pages" ) ." <input type='checkbox' name='types[]' value='comments' /> " . __( "Comments " ) ."<br />"; |
---|
265 | |
---|
266 | // author text box |
---|
267 | $author_output .= "<br />" . __( "Author:" ) ." <input type='text' value='' name=\"author\" />"; |
---|
268 | |
---|
269 | // category and tag/taxonomy selector (multiple choice box) |
---|
270 | $tax_output = ""; |
---|
271 | |
---|
272 | foreach( get_object_taxonomies('post') as $taxonomy ) { |
---|
273 | $data = get_taxonomy( $taxonomy ); |
---|
274 | |
---|
275 | if( $data->name == "category" ) |
---|
276 | $tax_output = "<br /> <br /> " . __( "Categories: " ) ."<br /><br />" . str_replace( "name='cat'", "name='cats[]' multiple='multiple' size='10'", wp_dropdown_categories( 'hierarchical=1&echo=0' ) ); |
---|
277 | |
---|
278 | else { |
---|
279 | $terms = get_terms( $data->name ); |
---|
280 | |
---|
281 | if( count ( $terms ) != 0 ) { |
---|
282 | $tax_output .= "<br /> <br /> " . $data->label . "<br /><br /><select name='tags[]' multiple='multiple' size='10'>"; |
---|
283 | foreach( $terms as $term ) { |
---|
284 | $tax_output .= "<option value='".$term->term_id."'>" . $term->name . '</option>'; |
---|
285 | } |
---|
286 | $tax_output .= "</select>"; |
---|
287 | } |
---|
288 | } |
---|
289 | } |
---|
290 | |
---|
291 | // Month Picker, created once and used for start and end dates |
---|
292 | $month_picker = "<option value=''>" . __( 'Select Month' ) . "</option> |
---|
293 | <option value='1'>" . __( 'January' ) . "</option> |
---|
294 | <option value='2'>" . __( 'February' ) . "</option> |
---|
295 | <option value='3'>" . __( 'March' ) . "</option> |
---|
296 | <option value='4'>" . __( 'April' ) . "</option> |
---|
297 | <option value='5'>" . __( 'May' ) . "</option> |
---|
298 | <option value='6'>" . __( 'June' ) . "</option> |
---|
299 | <option value='7'>" . __( 'July' ) . "</option> |
---|
300 | <option value='8'>" . __( 'August' ) . "</option> |
---|
301 | <option value='9'>" . __( 'September' ) . "</option> |
---|
302 | <option value='10'>" . __( 'October' ) . "</option> |
---|
303 | <option value='11'>" . __( 'November' ) . "</option> |
---|
304 | <option value='12'>" . __( 'December' ) . "</option>"; |
---|
305 | |
---|
306 | // Start date dropdowns |
---|
307 | $date_output = "<br /><br />" . __( 'From' ) . "<br /> <select name='startMonth'>{$month_picker}</select> |
---|
308 | <select name='startYear'> <option value=\"\">" . __( 'Select Year' ) . "</option> |
---|
309 | " . str_replace( get_option( "siteurl" ) . "/?m=", "", wp_get_archives( 'type=yearly&format=option&show_post_count=0&echo=0') ) . "</select> |
---|
310 | <select name='startDay'> <option value=\"\">" . __( 'Select Day' ) . "</option>"; |
---|
311 | |
---|
312 | for ( $i = 1; $i <= 31; $i++ ) { |
---|
313 | $date_output .= "<option value='{$i}'>{$i}</option>"; |
---|
314 | } |
---|
315 | |
---|
316 | $date_output .= "</select>"; |
---|
317 | |
---|
318 | // End date dropdowns |
---|
319 | $date_output .= "<br />" . __( 'to' ) . "<br /> <select name='endMonth'>{$month_picker}</select> |
---|
320 | <select name='endYear'> <option value=\"\">" . __( 'Select Year' ) . "</option> |
---|
321 | " . str_replace( get_option( "siteurl" ) . "/?m=", "", wp_get_archives( 'type=yearly&format=option&show_post_count=0&echo=0' ) ) . "</select> |
---|
322 | <select name='endDay'> <option value=\"\">" . __( 'Select Day' ) . "</option>"; |
---|
323 | |
---|
324 | for ( $i = 1; $i <= 31; $i++ ) { |
---|
325 | $date_output .= "<option value='{$i}'>{$i}</option>"; |
---|
326 | } |
---|
327 | |
---|
328 | $date_output .= "</select>"; |
---|
329 | |
---|
330 | // Return the final output |
---|
331 | |
---|
332 | // FILTER: search_advanced Allows you to edit the output of the advanced search page |
---|
333 | return apply_filters( 'search_advanced', "<br /><form method='get' id='result_search_form' action='' style='text-align: left;'> |
---|
334 | <input type='text' value='{$this->flags['string']}' name='s' id='results' size='50' /> |
---|
335 | {$option_output} |
---|
336 | {$author_output} |
---|
337 | {$tax_output} |
---|
338 | {$date_output} |
---|
339 | <p><input type='submit' id='searchsubmit' value='" . __( 'Search' ) . "' /></p> |
---|
340 | </form>" ); |
---|
341 | } |
---|
342 | |
---|
343 | /** |
---|
344 | * Create Flags |
---|
345 | * Cleans up input from the query string and sticks it in an easier to digest format for search plugins |
---|
346 | * @return array An array of search parameters |
---|
347 | */ |
---|
348 | function create_flags() { |
---|
349 | global $wpdb; |
---|
350 | // Clean up and separate the terms |
---|
351 | $this->flags['string'] = $wpdb->escape( $_GET['s'] ); |
---|
352 | preg_match_all( '/".*?("|$)|((?<=[\\s",+])|^)[^\\s",+]+/', $this->flags['string'], $matches ); |
---|
353 | $this->flags['terms'] = array_map( create_function( '$a', 'return trim($a, "\\"\'\\n\\r ");' ), $matches[0] ); |
---|
354 | |
---|
355 | // Clean up the start and end dates |
---|
356 | |
---|
357 | $this->flags['startYear'] = intval( $_GET['startYear'] ); |
---|
358 | $this->flags['startMonth'] = intval( $_GET['startMonth'] ); |
---|
359 | $this->flags['startDay'] = intval( $_GET['startDay'] ); |
---|
360 | |
---|
361 | $this->flags['endYear'] = intval( $_GET['endYear'] ); |
---|
362 | $this->flags['endMonth'] = intval( $_GET['endMonth'] ); |
---|
363 | $this->flags['endDay'] = intval( $_GET['endDay'] ); |
---|
364 | |
---|
365 | // Clean up the author flag |
---|
366 | $this->flags['author'] = $wpdb->escape( $_GET['authors'] ); |
---|
367 | |
---|
368 | // Clean up categories |
---|
369 | if( is_array( $_GET['cats'] ) ) { |
---|
370 | foreach( $_GET['cats'] as $i ) |
---|
371 | $this->flags['categories'][] = intval( $i ); |
---|
372 | } |
---|
373 | |
---|
374 | // Clean up tags |
---|
375 | if( is_array( $_GET['tags'] ) ) { |
---|
376 | foreach( $_GET['tags'] as $i ) |
---|
377 | $this->flags['tags'][] = intval( $i ); |
---|
378 | } |
---|
379 | |
---|
380 | // Clean up the content types (all, posts, pages, comments) |
---|
381 | if( is_array( $_GET['types'] ) ) { |
---|
382 | foreach( $_GET['types'] as $i ) { |
---|
383 | $this->flags['types'][] = $wpdb->escape( $i ); |
---|
384 | } |
---|
385 | } |
---|
386 | |
---|
387 | // Build our sort by/order by flag |
---|
388 | $this->flags['sort'] = "relevance"; |
---|
389 | if( $_GET['sort'] == "date" ) |
---|
390 | $this->flags['sort'] = "date"; |
---|
391 | elseif( $_GET['sort'] == "alpha" ) |
---|
392 | $this->flags['sort'] = "alpha"; |
---|
393 | |
---|
394 | |
---|
395 | // Decide ascending or decesnding |
---|
396 | $this->flags['sorttype'] = "DESC"; |
---|
397 | if( $_GET['sorttype'] == "ASC" ) |
---|
398 | $this->flags['sorttype'] = "ASC"; |
---|
399 | |
---|
400 | // decie which page we are on |
---|
401 | $this->flags['page'] = 1; |
---|
402 | if( !empty( $_GET['pg'] ) ) |
---|
403 | $this->flags['page'] = intval( $_GET['pg'] ); |
---|
404 | |
---|
405 | // return a mass array of all the flags/query strings |
---|
406 | // FILTER: search_flags Allows you to make any changes to the search flags array |
---|
407 | return apply_filters( 'search_flags', $this->flags ); |
---|
408 | } |
---|
409 | |
---|
410 | |
---|
411 | /** |
---|
412 | * Result Search Box |
---|
413 | * This function generates HTML for options to be displayed on a search result page. This includes options for filtering by type, reordering results and an |
---|
414 | * advanced search link |
---|
415 | * @return string HTML output for the various form controls |
---|
416 | */ |
---|
417 | function result_search_box( ) { |
---|
418 | $output = ""; |
---|
419 | |
---|
420 | // Return filters if the feature is enabled. flters are for filtering by content type (posts, pages, comments) |
---|
421 | if( $this->plugin->options['filters'] == 1 ) { |
---|
422 | $option_output = "<br />"; |
---|
423 | |
---|
424 | if( is_array( $this->flags['types'] ) ) { |
---|
425 | if( in_array( 'posts', $this->flags['types'] ) ) |
---|
426 | $checked['post'] = ' checked="checked"'; |
---|
427 | |
---|
428 | if( in_array( 'pages', $this->flags['types'] ) ) |
---|
429 | $checked['page'] = ' checked="checked"'; |
---|
430 | |
---|
431 | if( in_array( 'comments', $this->flags['types'] ) ) |
---|
432 | $checked['comment'] = ' checked="checked"'; |
---|
433 | } |
---|
434 | |
---|
435 | // all three types are searched at once |
---|
436 | else { |
---|
437 | $checked['post'] = ' checked="checked"'; |
---|
438 | $checked['page'] = ' checked="checked"'; |
---|
439 | $checked['comment'] = ' checked="checked"'; |
---|
440 | } |
---|
441 | |
---|
442 | // Find the current url for use within javascript (the switch sort and switch sort type dropdowns) |
---|
443 | $current_url = "http" . ( empty( $_SERVER["HTTPS"] ) ? "": ( $_SERVER["HTTPS"]=='on' ) ? "s": "" )."://" . ( $_SERVER["HTTP_HOST"] ) . ( $_SERVER["REQUEST_URI"] ); |
---|
444 | |
---|
445 | |
---|
446 | // Start building the options output |
---|
447 | $option_output .= __( 'Search Only:' ) . "<input type='checkbox' name='types[]' value='posts'{$checked['post']} /> " . __( 'Posts' ) . " |
---|
448 | <input type='checkbox' name='types[]' value='pages'{$checked['page']}/> " . __( 'Pages ' ) . " |
---|
449 | <input type='checkbox' name='types[]' value='comments'{$checked['comment']} /> " . __( 'Comments ') . "<br />"; |
---|
450 | } |
---|
451 | |
---|
452 | if( $this->plugin->options['sort'] == 1 ) { |
---|
453 | |
---|
454 | // Decide which sort flag is chosen |
---|
455 | if( $this->flags['sort'] == "date" ) |
---|
456 | $checked['date'] = ' selected="selected"'; |
---|
457 | elseif( $this->flags['sort'] == "alpha" ) |
---|
458 | $checked['alpha'] = ' selected="selected"'; |
---|
459 | elseif( $this->flags['sort'] == "relevance" ) |
---|
460 | $checked['relevance'] = ' selected="selected"'; |
---|
461 | |
---|
462 | // Decide which sort type flag is chosen |
---|
463 | if( $this->flags['sorttype'] == "ASC" ) |
---|
464 | $checked['ASC'] = ' selected="selected"'; |
---|
465 | elseif( $this->flags['sorttype'] == "DESC" ) |
---|
466 | $checked['DESC'] = ' selected="selected"'; |
---|
467 | |
---|
468 | // Start building the options output |
---|
469 | $option_output .= "<script type='text/javascript'> |
---|
470 | function switchSort(value) { |
---|
471 | document.location.href = '" . preg_replace( "/&sort=([A-Za-z]+)/i", "", $current_url ) . "'+'&sort='+value; |
---|
472 | } |
---|
473 | |
---|
474 | function switchSortType(value) { |
---|
475 | document.location.href = '" . preg_replace( "/&sorttype=([A-Za-z]+)/i", "", $current_url ) . "'+'&sorttype='+value; |
---|
476 | } |
---|
477 | </script> |
---|
478 | |
---|
479 | <br /><div style='text-align: right;'>" . __( 'Order results by:' ) . " |
---|
480 | |
---|
481 | <select name='sort' onChange='switchSort(this.options[this.selectedIndex].value);'>\n |
---|
482 | <option value='relevance'{$checked['relevance']}>" . __( 'Relevance' ) . "</option> |
---|
483 | <option value='date'{$checked['date']}>" . __( 'Date' ) . "</option> |
---|
484 | <option value='alpha'{$checked['alpha']}>" . __( 'Alphabetical' ) . "</option> |
---|
485 | </select> |
---|
486 | |
---|
487 | <select name='sorttype' onChange='switchSortType(this.options[this.selectedIndex].value);'>\n |
---|
488 | <option value='ASC'{$checked['ASC']}>" . __( 'Ascending' ) . "</option> |
---|
489 | <option value='DESC'{$checked['DESC']}>" . __( 'Descending' ) . "</option> |
---|
490 | </select></div>"; |
---|
491 | |
---|
492 | } |
---|
493 | |
---|
494 | if( $this->plugin->options['advanced'] == 1 ) |
---|
495 | $advanced = $this->advanced_link(); |
---|
496 | |
---|
497 | // Result Box Output |
---|
498 | $output = "<br /><form method='get' id='resultsearchform' action='' style='text-align: left;'> |
---|
499 | <input type='text' value='" . esc_attr( apply_filters( 'the_search_query', get_search_query() ) ) . "' name='s' id='results' /> |
---|
500 | <input type='submit' id='searchsubmit' value='" . __( 'Search' ) . "' /> |
---|
501 | {$advanced} |
---|
502 | {$option_output} |
---|
503 | </form>"; |
---|
504 | |
---|
505 | // FILTER: search_result_box Allows you to make changes to the filter/order box above the search results |
---|
506 | return apply_filters( 'search_result_box', $output ); |
---|
507 | } |
---|
508 | |
---|
509 | /** |
---|
510 | * Trim Excerpt |
---|
511 | * If a line of text is longer then $chars characters this functon shortens a line of text and returns it with $end at the end. If a line of text is shorter the whole |
---|
512 | * thing is returned. |
---|
513 | * @param string $content The text to shorten |
---|
514 | * @param int $chars The amount of characters to start shortening content at |
---|
515 | * @param string $end The characters to append to the end of the shortend text |
---|
516 | * @return string The shortend text with appended characters OR full already short text |
---|
517 | */ |
---|
518 | function trim_excerpt( $content, $chars = 150, $end = "..." ) { |
---|
519 | $content = substr( strip_tags( trim( $content ) ), 0, $chars ); |
---|
520 | $content = substr( $content, 0, strrpos( $content, ' ' ) ) . $end; |
---|
521 | apply_filters( 'the_excerpt', $content ); |
---|
522 | return $content; |
---|
523 | } |
---|
524 | |
---|
525 | /** |
---|
526 | * Execute Search |
---|
527 | * This function creates a virtual page and then replaces a shortcode [search] with output from another search plugin (the plugin's search() function). This function also |
---|
528 | * outputs all final HTML to the browser for search results. |
---|
529 | * @global class WordPress's DB Abstraction Class |
---|
530 | * @global class WordPress query object (from wp-includes/query.php). used for overloading "the loop" to display in a page. |
---|
531 | */ |
---|
532 | function execute_search() { |
---|
533 | global $wpdb, $wp_query; |
---|
534 | |
---|
535 | // Default the loop to nothing since we are trying to overload the page template |
---|
536 | $wp_query->posts = NULL; |
---|
537 | |
---|
538 | // Try to load the search page which SHOULD contain the short code [search] |
---|
539 | $wp_query->posts = $wpdb->get_results( "SELECT * from {$wpdb->prefix}posts WHERE post_type = 'page' and post_title = 'search'" ); |
---|
540 | |
---|
541 | // Create a virtual page if the search page does not exist |
---|
542 | if( empty( $wp_query->posts[0] ) ) { |
---|
543 | $object = new stdClass(); |
---|
544 | $object->post_title = __( "Search" ); |
---|
545 | $object->post_content = "[search]"; |
---|
546 | $wp_query->posts[0] = $object; |
---|
547 | } |
---|
548 | |
---|
549 | // Parse the shortodes for the loop |
---|
550 | add_shortcode( 'search', array( $this->plugin, 'search' ) ); |
---|
551 | |
---|
552 | // Return the template |
---|
553 | // Return the template |
---|
554 | $template = get_page_template(); |
---|
555 | |
---|
556 | if ( $template ) |
---|
557 | include $template; |
---|
558 | else |
---|
559 | include(TEMPLATEPATH . "/index.php"); |
---|
560 | |
---|
561 | exit; |
---|
562 | } |
---|
563 | |
---|
564 | } |
---|
565 | |
---|
566 | // END SEARCH API |
---|
567 | |
---|
568 | // BEGIN SEARCH INDEX CLASS |
---|
569 | |
---|
570 | /** |
---|
571 | * Search Index |
---|
572 | * This class contains a set of fucntions for "refreshing" (updating) the search index when a piece of content is changed in WordPress. |
---|
573 | * The purpose is to only need to search one table instead of 3+ for different pieces of content, have a standard set of data and make it easier for |
---|
574 | * search libraries like Sphinx to read. |
---|
575 | * @verson 1.0.0 |
---|
576 | */ |
---|
577 | class search_index { |
---|
578 | |
---|
579 | function search_index() { |
---|
580 | add_filter( 'delete_post', array( &$this, 'delete_post' ) ); |
---|
581 | add_filter( 'delete_comment', array( &$this, 'delete_comment' ) ); |
---|
582 | add_filter( 'save_post', array( &$this, 'save_post' ) ); |
---|
583 | add_filter( 'comment_post', array( &$this, 'comment_post' ) ); |
---|
584 | add_filter( 'edit_comment', array( &$this, 'edit_comment' ) ); |
---|
585 | add_filter( 'wp_set_comment_status', array( &$this, 'edit_comment' ) ); |
---|
586 | } |
---|
587 | /** |
---|
588 | * Delete Post |
---|
589 | * This function is ran when a post or a page is deleted from WordPress. The post or page is then also removed from being searched. |
---|
590 | * This function can be hooked into with the hook 'refreshed_search_index' |
---|
591 | * @param int $id The id of the post or page being deleted |
---|
592 | * @global object WordPress Database Abstraction Layer |
---|
593 | */ |
---|
594 | function delete_post( $id ) { |
---|
595 | global $wpdb; |
---|
596 | $wpdb->query( "DELETE FROM {$wpdb->prefix}search_index WHERE type = 'post' OR type = 'page' AND object = '" . $wpdb->escape( $id ) . "'" ); |
---|
597 | do_action( 'refreshed_search_index' ); |
---|
598 | } |
---|
599 | |
---|
600 | /** |
---|
601 | * Delete Comment |
---|
602 | * This function is ran when a comment is deleted from WordPress. The comment is then also removed from being searched. |
---|
603 | * This function can be hooked into with the hook 'refreshed_search_index' |
---|
604 | * @param int $id The id of the comment being deleted |
---|
605 | * @global object WordPress Database Abstraction Layer |
---|
606 | */ |
---|
607 | function delete_comment( $id ) { |
---|
608 | global $wpdb; |
---|
609 | $wpdb->query( "DELETE FROM {$wpdb->prefix}search_index WHERE type = 'comment' AND object = '" . intval( $id ) . "'" ); |
---|
610 | do_action( 'refreshed_search_index' ); |
---|
611 | } |
---|
612 | |
---|
613 | /** |
---|
614 | * Save Post |
---|
615 | * This function is ran when a page or post is saved or created in WordPress. The data or changed data is then made avaiable for searching. |
---|
616 | * This function can be hooked into with the hook 'refreshed_search_index' |
---|
617 | * @param int $id The id of the page or post |
---|
618 | * @global object WordPress Database Abstraction Layer |
---|
619 | */ |
---|
620 | function save_post( $id ) { |
---|
621 | global $wpdb; |
---|
622 | |
---|
623 | $post = $wpdb->get_results ( "SELECT * from {$wpdb->prefix}posts WHERE ID = '" . intval( $id ) . "'" ); |
---|
624 | |
---|
625 | // print_r($post); |
---|
626 | if( $post[0]->post_type != "revision" ) |
---|
627 | { |
---|
628 | $type = $post[0]->post_type; |
---|
629 | |
---|
630 | // Do we have an index for this (are we updating?) or are we creating |
---|
631 | $idx = $wpdb->get_results( "SELECT * from {$wpdb->prefix}search_index WHERE object = '" . intval( $post[0]->ID ) . "' AND ( type = 'post' OR type='page' ) " ); |
---|
632 | |
---|
633 | // Build a string of categories and tags that this object belongs to |
---|
634 | |
---|
635 | $cats = ","; |
---|
636 | $tags = ","; |
---|
637 | |
---|
638 | $terms = $wpdb->get_results( "SELECT r.term_taxonomy_id,t.taxonomy,r.object_id from $wpdb->term_relationships r LEFT JOIN $wpdb->term_taxonomy t ON(t.term_taxonomy_id=r.term_taxonomy_id) WHERE r.object_id = '" . intval( $post[0]->ID ) . "'" ); |
---|
639 | |
---|
640 | foreach( $terms as $term ) { |
---|
641 | if( $term->taxonomy == "category" ) |
---|
642 | $cats .= intval( $term->term_taxonomy_id ) . ","; |
---|
643 | else |
---|
644 | $tags .= intval( $term->term_taxonomy_id ) . ","; |
---|
645 | } |
---|
646 | |
---|
647 | // Find which author created this object |
---|
648 | $authors = $wpdb->get_results( "SELECT display_name FROM $wpdb->users WHERE ID = '" . intval( $post[0]->post_author ) ."'" ); |
---|
649 | $author = $authors[0]->display_name; |
---|
650 | |
---|
651 | // Can this item be searched yet? |
---|
652 | if( $post[0]->post_status == "publish" && empty( $post[0]->post_password ) ) |
---|
653 | $protected = "0"; |
---|
654 | else |
---|
655 | $protected = "1"; |
---|
656 | |
---|
657 | // update |
---|
658 | if( !empty( $idx[0]->id ) ) { |
---|
659 | $wpdb->query( "UPDATE {$wpdb->prefix}search_index SET title = '" . $wpdb->escape( $post[0]->post_title ) . "', content = '" . $wpdb->escape( $post[0]->post_content ) . "', post_date = '" . $wpdb->escape( $post[0]->post_date ) . "', parent = '', categories = '" . $wpdb->escape ( $cats ) . "', tags = '" . $wpdb->escape( $tags ) . "', author = '" . $wpdb->escape ( $author ) . "', type = '" . $wpdb->escape( $type ) . "', protected = '" . $wpdb->escape ( $protected ) . "' WHERE object = '" . intval( $post[0]->ID ) . "'" ); |
---|
660 | } |
---|
661 | |
---|
662 | // create new |
---|
663 | else { |
---|
664 | $wpdb->query( "INSERT INTO {$wpdb->prefix}search_index (object, title, content, post_date, parent, categories, tags, author, type, protected) |
---|
665 | VALUES ( |
---|
666 | '" . intval( $id ) . "', '" . $wpdb->escape( $post[0]->post_title ) . "', '" . $wpdb->escape( $post[0]->post_content ) . "', '" . $wpdb->escape( $post[0]->post_date ) . "', '0', '" . $wpdb->escape( $cat ) . "', '" . $wpdb->escape( $tags ) . "', '" . $wpdb->escape( $author ) . "', '" . $wpdb->escape( $type ) . "', '" . $wpdb->escape( $protected ) . "' |
---|
667 | );" ); |
---|
668 | |
---|
669 | } |
---|
670 | do_action( 'refreshed_search_index' ); |
---|
671 | } |
---|
672 | } |
---|
673 | |
---|
674 | /** |
---|
675 | * Comment Post |
---|
676 | * This function is ran when a comment is made on a post in WordPress. The comment is made avaiable for searching. |
---|
677 | * This function can be hooked into with the hook 'refreshed_search_index' |
---|
678 | * @param int $id The id of the of the comment |
---|
679 | * @global object WordPress Database Abstraction Layer |
---|
680 | */ |
---|
681 | function comment_post( $id ) { |
---|
682 | global $wpdb; |
---|
683 | |
---|
684 | // Load the comment |
---|
685 | $comment = $wpdb->get_results( "SELECT * from {$wpdb->prefix}comments WHERE comment_ID = '" . intval( $id ) . "'" ); |
---|
686 | |
---|
687 | // Load the straight post data |
---|
688 | $post = $wpdb->get_results( "SELECT * from {$wpdb->prefix}posts WHERE ID = '" . intval( $comment[0]->comment_post_ID ) . "'" ); |
---|
689 | |
---|
690 | // Load a string of the cats the comment is in |
---|
691 | $cat = ","; |
---|
692 | $cats = $wpdb->get_results( "SELECT term_taxonomy_id from $wpdb->term_relationships WHERE object_id = '" . intval( $comment[0]->comment_post_ID ) . "'" ); |
---|
693 | |
---|
694 | foreach( $cats[0] as $catz ) { |
---|
695 | $cat .= $catz.","; |
---|
696 | } |
---|
697 | |
---|
698 | // Can we search this comment? |
---|
699 | if( $comment[0]->comment_approved == 1 ) |
---|
700 | $protected = 0; |
---|
701 | else |
---|
702 | $protected = 1; |
---|
703 | |
---|
704 | // create the index entry |
---|
705 | $wpdb->query( "INSERT INTO {$wpdb->prefix}search_index (object, title, content, post_date, parent, categories, author, type, protected) |
---|
706 | VALUES ( |
---|
707 | '" . intval( $comment[0]->comment_ID ) . "', '', '" . $wpdb->escape( $comment[0]->comment_content ) . "', '" . $wpdb->escape( $comment[0]->comment_date ) . "', '". intval( $post[0]->ID ) ."', '" . $wpdb->escape( $cat ) . "', '" . $wpdb->escape( $comment[0]->comment_author ) . "', 'comment', '" . $wpdb->escape( $protected ) . "' |
---|
708 | );" ); |
---|
709 | |
---|
710 | do_action( 'refreshed_search_index' ); |
---|
711 | } |
---|
712 | |
---|
713 | |
---|
714 | /** |
---|
715 | * Edit Comment |
---|
716 | * This function is ran when a comment is changed. The updated comment is made avaiable for searching. |
---|
717 | * This function can be hooked into with the hook 'refreshed_search_index' |
---|
718 | * @param int $id The id of the of the comment |
---|
719 | * @param string $status The status of the comment (closed, etc) |
---|
720 | * @global object WordPress Database Abstraction Layer |
---|
721 | */ |
---|
722 | function edit_comment( $id, $status = '' ) { |
---|
723 | global $wpdb; |
---|
724 | |
---|
725 | // Load the comment data |
---|
726 | $comment = $wpdb->get_results( "SELECT * from {$wpdb->prefix}comments WHERE comment_ID = '" . intval( $id ) . "'" ); |
---|
727 | |
---|
728 | // Load the post data for this comment |
---|
729 | $post = $wpdb->get_results( "SELECT * from {$wpdb->prefix}posts WHERE ID = '" . intval( $comment[0]->comment_post_ID ) . "'" ); |
---|
730 | |
---|
731 | // Load a string of the categories the comment is in |
---|
732 | $cat = ","; |
---|
733 | $cats = $wpdb->get_results( "SELECT term_taxonomy_id from $wpdb->term_relationships WHERE object_id = '". intval( $post[0]->ID ) . "'" ); |
---|
734 | |
---|
735 | if( is_array( $cats[0] ) ) |
---|
736 | { |
---|
737 | foreach( $cats[0] as $catz ) { |
---|
738 | $cat .= $catz->term_taxonomy_id.","; |
---|
739 | } |
---|
740 | } |
---|
741 | |
---|
742 | // see if we can search this comment |
---|
743 | if( $comment[0]->comment_approved ) |
---|
744 | $protected = 0; |
---|
745 | else |
---|
746 | $protected = 1; |
---|
747 | |
---|
748 | // update the entry |
---|
749 | $wpdb->query( "UPDATE {$wpdb->prefix}search_index SET title = '', content = '" . $wpdb->escape( $comment[0]->comment_content ) . "', post_date = '" . $wpdb->escape( $comment[0]->comment_date ) . "', parent = '" . intval( $post[0]->ID ) . "', categories = '" . $wpdb->escape( $cat ) . "', author = '" . $wpdb->escape( $comment[0]->comment_author ) . "', protected = '" . $wpdb->escape( $protected ) . "' WHERE object = '" . intval( $id ) . "' AND type = 'comment'" ); |
---|
750 | |
---|
751 | do_action( 'refreshed_search_index' ); |
---|
752 | } |
---|
753 | |
---|
754 | /** |
---|
755 | * Refresh All |
---|
756 | * This function is database resource intensive. It clears out the entire search index and rebuilds it. |
---|
757 | * This function can be hooked into with the hook 'refreshed_search_index' |
---|
758 | * @global object WordPress Database Abstraction Layer |
---|
759 | */ |
---|
760 | function all() { |
---|
761 | global $wpdb; |
---|
762 | |
---|
763 | // delete the current index |
---|
764 | $wpdb->query( "DELETE FROM {$wpdb->prefix}search_index" ); |
---|
765 | |
---|
766 | // Load all the posts and pages stored within WordPress |
---|
767 | $posts = $wpdb->get_results( "SELECT * from {$wpdb->prefix}posts" ); |
---|
768 | |
---|
769 | foreach( $posts as $post ) { |
---|
770 | // don't search revisions |
---|
771 | if( $post->post_type == "page" || $post->post_type == "post" ) { |
---|
772 | |
---|
773 | // Build a string of categories and tags that this object belongs to |
---|
774 | $cats = ","; |
---|
775 | $tags = ","; |
---|
776 | |
---|
777 | $terms = $wpdb->get_results( "SELECT r.term_taxonomy_id,t.taxonomy,r.object_id from $wpdb->term_relationships r LEFT JOIN $wpdb->term_taxonomy t ON(t.term_taxonomy_id=r.term_taxonomy_id) WHERE r.object_id = '" . intval( $post->ID ) . "'" ); |
---|
778 | |
---|
779 | foreach( $terms as $term ) { |
---|
780 | if( $term->taxonomy == "category" ) |
---|
781 | $cats .= intval( $term->term_taxonomy_id ) . ","; |
---|
782 | else |
---|
783 | $tags .= intval( $term->term_taxonomy_id ) . ","; |
---|
784 | } |
---|
785 | |
---|
786 | // find the author |
---|
787 | $authors = $wpdb->get_results( "SELECT display_name FROM $wpdb->users WHERE ID = '" . intval( $post->post_author ) . "'" ); |
---|
788 | $author = $authors[0]->display_name; |
---|
789 | |
---|
790 | // Can we search this item? |
---|
791 | if( $post->post_status == "publish" && empty( $post->post_password ) ) |
---|
792 | $protected = "0"; |
---|
793 | else |
---|
794 | $protected = "1"; |
---|
795 | |
---|
796 | // Create the entry in the search index |
---|
797 | $wpdb->query( "INSERT INTO {$wpdb->prefix}search_index (object, title, content, post_date, parent, categories, tags, author, type, protected) |
---|
798 | VALUES ( |
---|
799 | '". intval( $post->ID ) . "', '" . $wpdb->escape( $post->post_title ) . "', '" . $wpdb->escape( $post->post_content ) . "', '" . $wpdb->escape( $post->post_date ) . "', '0', '" . $wpdb->escape( $cats ) . "', '" . $wpdb->escape( $tags ) . "', '" . $wpdb->escape( $author ) . "', '" . $wpdb->escape( $post->post_type ) . "', '" . $wpdb->escape( $protected ) . "' |
---|
800 | );" ); |
---|
801 | |
---|
802 | do_action( 'refreshed_search_index' ); |
---|
803 | } |
---|
804 | } |
---|
805 | |
---|
806 | // Load up the comments for insertion now |
---|
807 | $comments = $wpdb->get_results( "SELECT * from {$wpdb->prefix}comments" ); |
---|
808 | |
---|
809 | foreach($comments as $comment) { |
---|
810 | // load the post data |
---|
811 | $post = $wpdb->get_results( "SELECT * from {$wpdb->prefix}posts WHERE ID = '" . intval( $comment->comment_post_ID ) . "'" ); |
---|
812 | |
---|
813 | // load a string of categories that the comment is in |
---|
814 | $cat = ","; |
---|
815 | $cats = $wpdb->get_results( "SELECT term_taxonomy_id from $wpdb->term_relationships WHERE object_id = '" . intval( $comment->comment_post_ID ) . "'" ); |
---|
816 | |
---|
817 | foreach( $cats as $catz ) { |
---|
818 | $cat .= intval( $catz->term_taxonomy_id ) . ","; |
---|
819 | } |
---|
820 | |
---|
821 | // is the comment searchable |
---|
822 | if( $comment->comment_approved ) |
---|
823 | $protected = 0; |
---|
824 | else |
---|
825 | $protected = 1; |
---|
826 | |
---|
827 | // create the search index row |
---|
828 | $wpdb->query( "INSERT INTO {$wpdb->prefix}search_index (object, title, content, post_date, parent, categories, author, type, protected) |
---|
829 | VALUES ( |
---|
830 | '" . intval( $comment->comment_ID ) . "', '', '" . $wpdb->escape( $comment->comment_content ) . "', '" . $wpdb->escape( $comment->comment_date ) . "', '" . intval( $post[0]->ID ) . "', '" . $wpdb->escape( $cat ) . "', '" . $wpdb->escape( $comment->comment_author ) . "', 'comment', '" . $wpdb->escape( $protected ) . "' |
---|
831 | );" ); |
---|
832 | } |
---|
833 | |
---|
834 | } |
---|
835 | } |
---|
836 | // END SEARCH INDEX |
---|
837 | |
---|
838 | function load_search_api() { |
---|
839 | global $wpsearch; |
---|
840 | $wpsearch = new WP_Search(); |
---|
841 | add_action( 'admin_menu', array( &$wpsearch, 'options_admin_menu' ) ); |
---|
842 | } |
---|
843 | |
---|
844 | if ( ! isset($wpsearch) ) { |
---|
845 | add_action( 'plugins_loaded', 'load_search_api' ); |
---|
846 | } |
---|
847 | |
---|
848 | |
---|
849 | // Load the search index and have WordPress call the functions if we have indexing enabled |
---|
850 | if( $wpsearch->plugin->options['index'] == 1 ) |
---|
851 | $index = new search_index(); |
---|
852 | |
---|
853 | ?> |
---|