/[drupal]/contributions/modules/sphinxsearch/sphinxsearch.pages.inc
ViewVC logotype

Contents of /contributions/modules/sphinxsearch/sphinxsearch.pages.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.4 - (show annotations) (download) (as text)
Fri Sep 12 02:44:22 2008 UTC (14 months, 2 weeks ago) by markuspetrux
Branch: MAIN
CVS Tags: HEAD
Branch point for: DRUPAL-6--1
Changes since 1.3: +48 -19 lines
File MIME type: text/x-php
- Ported module from D5 to D6.
- Bugfix: undefined class method in sphinxsearch_check_connection_page().
- Bugfix: added criterion class to taxonomy elements in advanced search form.
- Bugfix: added support for mysqli and pgsql to _sphinxsearch_db_reconnect().
1 <?php
2 // $Id: sphinxsearch.pages.inc,v 1.1.2.11 2008/09/03 01:38:55 markuspetrux Exp $
3
4 /**
5 * @file
6 * Implementation of pages used to search using Sphinx search module.
7 */
8
9 /**
10 * Menu callback; presents the search form and/or search results.
11 *
12 * Search form submits with POST but redirects to GET. This way we can keep
13 * the search query URL clean as a whistle:
14 * http://www.example.com/search-path?keys=... (including keys and options)
15 * @see sphinxsearch_get_search_path()
16 * @see sphinxsearch_get_query_string()
17 */
18 function sphinxsearch_search_page() {
19 global $user;
20
21 if (isset($_POST['form_id'])) {
22 // Let POST method redirect to GET.
23 return drupal_get_form('sphinxsearch_search_form');
24 }
25
26 // Parse request and build search options structure.
27 $search_options = sphinxsearch_parse_request($_GET);
28
29 // If any error was found in request, display them and render expanded search form.
30 if (!empty($search_options['errors'])) {
31 foreach ($search_options['errors'] as $field_name => $message) {
32 form_set_error($field_name, $message);
33 }
34 return drupal_get_form('sphinxsearch_search_form', $search_options, FALSE);
35 }
36
37 // If no filters have specified, just render a clean collapsed search form.
38 // If form was submitted, user has already been warned from submit handler.
39 if (empty($search_options['filters'])) {
40 return drupal_get_form('sphinxsearch_search_form', $search_options);
41 }
42
43 // Flood control.
44 $admin_access = user_access('administer sphinxsearch');
45 if (!$admin_access) {
46 $rid = ($user->uid ? DRUPAL_AUTHENTICATED_RID : DRUPAL_ANONYMOUS_RID);
47 $threshold = (int)variable_get('sphinxsearch_search_flood_per_role_'. $rid, 0);
48 if ($threshold > 0 && !flood_is_allowed('sphinxsearch:search', $threshold)) {
49 flood_register_event('sphinxsearch:search');
50 // This header is specially aimed to stop bad bots.
51 header('HTTP/1.0 403 Forbidden');
52 // Enable flood limit flag to not generate tagadelic/faceted search blocks.
53 sphinxsearch_flood_limit_exceeded(TRUE);
54 return t('You cannot perform more than @number search queries per hour. Please try again later.', array('@number' => $threshold));
55 }
56 }
57
58 // Execute search query and collect the results.
59 $search_results = sphinxsearch_execute_query($search_options);
60
61 // If any, display warnings if user is administrator,
62 // or store them to watchdog for later review.
63 if (!empty($search_results['warnings'])) {
64 sphinxsearch_watchdog_warning(implode('<br />', $search_results['warnings']));
65 }
66
67 // Deal with search results.
68 if ($search_results['total_available'] <= 0) {
69 if (!empty($search_results['error_message'])) {
70 drupal_set_message($search_results['error_message'], 'error');
71 }
72 // Display no results warning and render expanded search form.
73 $output = theme('box', t('Your search yielded no results'), sphinxsearch_help('sphinxsearch#noresults'));
74 $output .= drupal_get_form('sphinxsearch_search_form', $search_options, FALSE);
75 }
76 else {
77 // Render collapsed search form and search results.
78 $output = drupal_get_form('sphinxsearch_search_form', $search_options);
79 $output .= theme('box', t('Search results'), theme('sphinxsearch_search_results', $search_options, $search_results));
80
81 // Register flood event for non-admins.
82 if (!$admin_access) {
83 flood_register_event('sphinxsearch:search');
84 }
85
86 // Log the search request?
87 $log_search_keys = variable_get('sphinxsearch_log_search_keys', 'always');
88 if ($log_search_keys == 'always' || ($log_search_keys == 'keys-only' && !empty($search_options['filters']['keys']))) {
89 $query_string = sphinxsearch_get_query_string($search_options);
90 watchdog('search', t('%keys %query', array(
91 '%keys' => (!empty($search_options['filters']['keys']) ? $search_options['filters']['keys'] : t('(no keywords)')),
92 '%query' => (!empty($query_string) ? '?'. urldecode($query_string) : ''),
93 )), NULL,
94 WATCHDOG_NOTICE,
95 l(t('results'), sphinxsearch_get_search_path(), array('query' => $query_string))
96 );
97 }
98 }
99
100 return $output;
101 }
102
103 /**
104 * Record a warning to watchdog.
105 *
106 * If current user has 'administer sphinxsearch' privilege,
107 * then the warning is just sent to the user interface.
108 *
109 * @param string $message
110 */
111 function sphinxsearch_watchdog_warning($message) {
112 if (user_access('administer sphinxsearch')) {
113 drupal_set_message($message, 'error');
114 }
115 else {
116 watchdog('sphinxsearch', $message, NULL, WATCHDOG_WARNING);
117 }
118 }
119
120 /**
121 * Render a search form.
122 *
123 * @param array $search_options
124 * The search string and options entered by the user.
125 * @param boolean $advanced_options_collapsed
126 * TRUE to collapse Advanced search options fieldset.
127 * @return array
128 * The search form.
129 */
130 function sphinxsearch_search_form(&$form_state, $search_options = array(), $advanced_options_collapsed = TRUE) {
131 $form = array(
132 '#action' => url(sphinxsearch_get_search_path()),
133 '#attributes' => array('class' => 'search-form'),
134 );
135 $form['basic'] = array('#type' => 'item', '#title' => t('Enter your keywords'));
136 $form['basic']['inline'] = array('#prefix' => '<div class="container-inline">', '#suffix' => '</div>');
137 $form['basic']['inline']['keys'] = array(
138 '#type' => 'textfield',
139 '#title' => '',
140 '#default_value' => $search_options['filters']['keys'],
141 '#size' => 50,
142 '#maxlength' => 255,
143 );
144 $form['basic']['inline']['submit'] = array('#type' => 'submit', '#value' => t('Search'));
145
146 $form['advanced'] = array(
147 '#type' => 'fieldset',
148 '#title' => t('Advanced search'),
149 '#collapsible' => TRUE, '#collapsed' => $advanced_options_collapsed,
150 '#attributes' => array('class' => 'search-advanced'),
151 );
152
153 // Matching modes.
154 $form['advanced']['matchmode'] = array(
155 '#type' => 'radios',
156 '#title' => t('Matching mode'),
157 '#prefix' => '<div class="criterion">',
158 '#suffix' => '</div>',
159 '#options' => sphinxsearch_get_matching_modes(),
160 '#default_value' => (isset($search_options['matchmode']) ? $search_options['matchmode'] : SPHINXSEARCH_MATCH_ALL),
161 );
162
163 // Filter by content author.
164 $form['advanced']['author'] = array(
165 '#type' => 'textfield',
166 '#title' => t('Author'),
167 '#prefix' => '<div class="criterion">',
168 '#suffix' => '</div>',
169 '#maxlength' => 60,
170 '#autocomplete_path' => 'user/autocomplete',
171 '#default_value' => (isset($search_options['filters']['author']['name']) ? $search_options['filters']['author']['name'] : ''),
172 '#description' => t('Use this option to filter search results by content author.'),
173 );
174
175 // Filter by content type (only if more than one content type exists).
176 $system_node_types = array_map('check_plain', node_get_types('names'));
177 $search_node_types = array();
178 foreach (sphinxsearch_get_enabled_node_types() as $type) {
179 $search_node_types[$type] = $system_node_types[$type];
180 }
181 if (count($search_node_types) > 1) {
182 $form['advanced']['types'] = array(
183 '#type' => 'checkboxes',
184 '#title' => t('Only of the type(s)'),
185 '#prefix' => '<div class="criterion">',
186 '#suffix' => '</div>',
187 '#options' => $search_node_types,
188 '#default_value' => (isset($search_options['filters']['types']) ? $search_options['filters']['types'] : array()),
189 );
190 }
191
192 // Filter by taxonomy (only if taxonomy module is enabled).
193 if (module_exists('taxonomy')) {
194 $vocabularies = sphinxsearch_get_enabled_vocabularies();
195 // Build filtering options for enabled vocabularies.
196 if (!empty($vocabularies)) {
197 $form['advanced']['taxonomy'] = array();
198 foreach ($vocabularies as $vid => $vocabulary) {
199 $terms_key = 'terms'. $vid;
200 $search_terms = (!empty($search_options['filters']['taxonomy'][$vid]) ? $search_options['filters']['taxonomy'][$vid] : array());
201 if ($vocabulary->tags) {
202 // Note: for some reason, titles for freetagging vocabularies are not
203 // check_plain'd in taxonomy module. Let's be consistent with core.
204 $form['advanced']['taxonomy']['tags'][$terms_key] = array(
205 '#type' => 'textfield',
206 '#title' => $vocabulary->name,
207 '#prefix' => '<div class="criterion">',
208 '#suffix' => '</div>',
209 '#description' => t('If you wish to filter by more than one term, you can separate them with commas. Example: funny, bungee jumping, "Company, Inc.".'),
210 '#default_value' => sphinxsearch_taxonomy_encode_typed_terms($search_terms),
211 '#autocomplete_path' => 'taxonomy/autocomplete/'. $vid,
212 '#weight' => $vocabulary->weight,
213 '#maxlength' => 255,
214 );
215 }
216 else {
217 $tree = taxonomy_get_tree($vid);
218 if ($tree) {
219 $options = array();
220 foreach ($tree as $term) {
221 $choice = new stdClass();
222 $choice->option = array($term->tid => str_repeat('-', $term->depth) . $term->name);
223 $options[] = $choice;
224 }
225 if (!empty($options)) {
226 $form['advanced']['taxonomy'][$terms_key] = array(
227 '#type' => 'select',
228 '#title' => check_plain($vocabulary->name),
229 '#prefix' => '<div class="criterion">',
230 '#suffix' => '</div>',
231 '#description' => t('Select one or more items to filter search results by terms of this category.'),
232 '#default_value' => array_keys($search_terms),
233 '#options' => $options,
234 '#multiple' => TRUE,
235 '#size' => min(10, count($options)),
236 '#weight' => $vocabulary->weight,
237 );
238 }
239 }
240 }
241 }
242 // Add fieldset only if form has more than 1 vocabulary element.
243 if (count($form['advanced']['taxonomy']) > 1) {
244 $form['advanced']['taxonomy'] += array(
245 '#type' => 'fieldset',
246 '#title' => t('Categories'),
247 '#prefix' => '<div class="clear-block"></div>',
248 '#suffix' => '<div class="clear-block"></div>',
249 '#collapsible' => TRUE, '#collapsed' => empty($search_options['filters']['taxonomy']),
250 );
251 }
252 }
253 }
254
255 // Sort options.
256 $form['advanced']['sortfield'] = array(
257 '#type' => 'radios',
258 '#title' => t('Order by'),
259 '#prefix' => '<div class="criterion">',
260 '#suffix' => '</div>',
261 '#options' => sphinxsearch_get_sortable_fields(),
262 '#default_value' => (isset($search_options['sortfield']) ? $search_options['sortfield'] : '@weight'),
263 );
264 $form['advanced']['sortdir'] = array(
265 '#type' => 'radios',
266 '#title' => t('Sort direction'),
267 '#prefix' => '<div class="criterion">',
268 '#suffix' => '</div>',
269 '#options' => array('ASC' => t('Ascending'), 'DESC' => t('Descending')),
270 '#default_value' => (isset($search_options['sortdir']) ? $search_options['sortdir'] : 'DESC'),
271 );
272
273 $form['advanced']['submit'] = array(
274 '#type' => 'submit',
275 '#value' => t('Advanced search'),
276 '#prefix' => '<div class="action">',
277 '#suffix' => '</div>',
278 );
279
280 return $form;
281 }
282
283 /**
284 * Validate a search form submission.
285 */
286 function sphinxsearch_search_form_validate($form, &$form_state) {
287 $search_options = sphinxsearch_parse_request($form_state['values']);
288 if (!empty($search_options['errors'])) {
289 foreach ($search_options['errors'] as $field_name => $message) {
290 form_set_error($field_name, $message);
291 }
292 }
293 }
294
295 /**
296 * Process a search form submission.
297 */
298 function sphinxsearch_search_form_submit($form, &$form_state) {
299 $search_options = sphinxsearch_parse_request($form_state['values']);
300 $query_string = sphinxsearch_get_query_string($search_options);
301 if (empty($query_string)) {
302 form_set_error('keys', t('Please enter some keywords and/or other search options.'));
303 }
304 // Transform POST into a GET request.
305 sphinxsearch_goto_search($query_string);
306 }
307
308 /**
309 * Format the results page for the given query results array.
310 *
311 * @param array $search_options
312 * Search options structure.
313 * @param array $search_results
314 * Search results structure.
315 *
316 * @ingroup themeable
317 */
318 function theme_sphinxsearch_search_results($search_options, $search_results) {
319 // Display information about query execution.
320 $output = '<p>';
321 $output .= format_plural($search_results['total_found'], '1 document found', '@count documents found') .' '. t('in @seconds seconds.', array('@seconds' => round($search_results['time'], 3)));
322 if (!empty($search_results['words'])) {
323 $words = array();
324 foreach ($search_results['words'] as $word => $word_data) {
325 $words[] = '<em>'. check_plain($word) .'</em> ('. t('documents: @docs, hits: @hits', array('@docs' => (int)$word_data['docs'], '@hits' => (int)$word_data['hits'])) .')';
326 }
327 $output .= ' '. t('Terms found:') .' '. implode('; ', $words) .'.';
328 }
329 $output .= '</p>' ."\n";
330 if ($search_results['total_found'] != $search_results['total_available']) {
331 drupal_set_message(t('Warning: only the first @count matches are displayed. You may wish to use additional filters to reduce the number of results.', array('@count' => $search_results['total_available'])), 'error');
332 }
333
334 // Display list of formatted search results.
335 $output .= '<dl class="search-results">' ."\n";
336 foreach ($search_results['nodes'] as $item_id => $node) {
337 $title = (isset($search_results['titles'][$item_id]) ? $search_results['titles'][$item_id] : check_plain($node->title));
338 $output .= '<dt class="title">'. l($title, 'node/'. $node->nid, array('html' => TRUE)) .'</dt>' ."\n";
339 $info = array(
340 theme('username', $node),
341 format_date($node->created, 'small'),
342 );
343 $extra = node_invoke_nodeapi($node, 'search result');
344 if (!empty($extra) && is_array($extra)) {
345 $info = array_merge($info, $extra);
346 }
347 $excerpt = (isset($search_results['excerpts'][$item_id]) ? '<p class="search-excerpt">'. $search_results['excerpts'][$item_id] .'</p>' ."\n" : '');
348 if (module_exists('taxonomy') && is_array($node->taxonomy)) {
349 $links = array();
350 foreach ($node->taxonomy as $term) {
351 $links[] = l($term->name, taxonomy_term_path($term));
352 }
353 if (!empty($links)) {
354 $excerpt .= '<p class="search-links">'. t('Tags') .': '. implode(' - ', $links) .'</p>' ."\n";
355 }
356 }
357 $output .= '<dd>'. $excerpt .'<p class="search-info">'. implode(' - ', $info) .'</p></dd>' ."\n";
358 }
359 $output .= '</dl>' ."\n";
360
361 // Display pager.
362 $output .= sphinxsearch_pager($search_results['total_available'], $search_options['results_per_page']);
363 return $output;
364 }

  ViewVC Help
Powered by ViewVC 1.1.2