/[drupal]/contributions/modules/apachesolr/apachesolr_search.module
ViewVC logotype

Contents of /contributions/modules/apachesolr/apachesolr_search.module

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


Revision 1.11 - (show annotations) (download) (as text)
Mon Jun 29 23:23:08 2009 UTC (4 months, 3 weeks ago) by pwolanin
Branch: MAIN
CVS Tags: HEAD
Changes since 1.10: +483 -248 lines
File MIME type: text/x-php
sync with DRUPAL-6--1
1 <?php
2 // $Id: apachesolr_search.module,v 1.1.2.6.2.104 2009/06/22 19:13:13 pwolanin Exp $
3
4 /**
5 * @file
6 * Provides a content search implementation for node content for use with the
7 * Apache Solr search application.
8 */
9
10 /**
11 * Implementation of hook_help().
12 */
13 function apachesolr_search_help($section) {
14 switch ($section) {
15 case 'admin/settings/apachesolr/index':
16 if (variable_get('apachesolr_read_only', 0)) {
17 return t('Operating in read-only mode; updates are disabled.');
18 }
19 $remaining = 0;
20 $total = 0;
21 // Collect the stats
22 $status = module_invoke('apachesolr_search', 'search', 'status');
23 $remaining += $status['remaining'];
24 $total += $status['total'];
25
26 return t('The search index is generated by !cron. %percentage of the site content has been sent to the server. There @items left to send.', array(
27 '!cron' => l(t('running cron'), 'admin/reports/status/run-cron', array('query' => array('destination' => 'admin/settings/apachesolr/index'))),
28 '%percentage' => ((int)min(100, 100 * ($total - $remaining) / max(1, $total))) .'%',
29 '@items' => format_plural($remaining, t('is 1 item'), t('are @count items')
30 )));
31 }
32 }
33
34 /**
35 * Implementation of hook_menu().
36 */
37 function apachesolr_search_menu() {
38 $items['admin/settings/apachesolr/query-fields'] = array(
39 'title' => 'Search fields',
40 'page callback' => 'apachesolr_search_settings_page',
41 'access arguments' => array('administer site configuration'),
42 'weight' => 1,
43 'type' => MENU_LOCAL_TASK,
44 'file' => 'apachesolr_search.admin.inc',
45 );
46 $items['admin/settings/apachesolr/content-bias'] = array(
47 'title' => 'Content bias settings',
48 'page callback' => 'apachesolr_boost_settings_page',
49 'access arguments' => array('administer site configuration'),
50 'weight' => 1,
51 'type' => MENU_LOCAL_TASK,
52 'file' => 'apachesolr_search.admin.inc',
53 );
54
55 return $items;
56 }
57
58 /**
59 * Implementation of hook_menu_alter().
60 */
61 function apachesolr_search_menu_alter(&$menu) {
62 if (isset($menu['search/apachesolr_search/%menu_tail'])) {
63 $menu['search']['page callback'] = 'apachesolr_search_view';
64 $menu['search/apachesolr_search/%menu_tail']['page callback'] = 'apachesolr_search_view';
65 }
66 if (variable_get('apachesolr_search_make_default', 0)) {
67 if (isset($menu['search/node/%menu_tail'])) {
68 // Hide the node search tab.
69 $menu['search/node/%menu_tail']['type'] = MENU_CALLBACK;
70 unset($menu['search/node/%menu_tail']['title callback'], $menu['search/node/%menu_tail']['title arguments']);
71 $menu['search/node/%menu_tail']['title'] = 'Search';
72 }
73 if (isset($menu['search/apachesolr_search/%menu_tail'])) {
74 // Alter the solr search tab
75 $menu['search/apachesolr_search/%menu_tail']['weight'] = -10;
76 unset($menu['search/apachesolr_search/%menu_tail']['title callback'], $menu['search/apachesolr_search/%menu_tail']['title arguments']);
77 $menu['search/apachesolr_search/%menu_tail']['title'] = 'Content';
78 }
79 if (isset($menu['search'])) {
80 $menu['search']['page arguments'] = array('apachesolr_search');
81 }
82 }
83 }
84
85 /**
86 * Implementation of hook_update_index().
87 */
88 function apachesolr_search_update_index() {
89 $cron_limit = variable_get('apachesolr_cron_limit', 50);
90 $rows = apachesolr_get_nodes_to_index('apachesolr_search', $cron_limit);
91 apachesolr_index_nodes($rows, 'apachesolr_search');
92 }
93
94 /**
95 * Implementation of hook_apachesolr_types_exclude().
96 */
97 function apachesolr_search_apachesolr_types_exclude($namespace) {
98 if ($namespace == 'apachesolr_search') {
99 $excluded_types = variable_get('apachesolr_search_excluded_types', array());
100 return array_filter($excluded_types);
101 }
102 }
103
104 /**
105 * Implementation of hook_search().
106 */
107 function apachesolr_search_search($op = 'search', $keys = NULL) {
108
109 switch ($op) {
110 case 'name':
111 return t('Search');
112
113 case 'reset':
114 apachesolr_clear_last_index('apachesolr_search');
115 return;
116
117 case 'status':
118 return apachesolr_index_status('apachesolr_search');
119
120 case 'search':
121 $filters = isset($_GET['filters']) ? $_GET['filters'] : '';
122 $solrsort = isset($_GET['solrsort']) ? $_GET['solrsort'] : '';
123 $page = isset($_GET['page']) ? $_GET['page'] : 0;
124 try {
125 $results = apachesolr_search_execute($keys, $filters, $solrsort, 'search/' . arg(1), $page);
126 return $results;
127 }
128 catch (Exception $e) {
129 watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
130 apachesolr_failure(t('Solr search'), $keys);
131 }
132 break;
133 } // switch
134 }
135
136 /**
137 * Re-implementation of search_view().
138 */
139 function apachesolr_search_view($type = NULL) {
140 // Search form submits with POST but redirects to GET.
141 $results = '';
142 if (!isset($_POST['form_id'])) {
143 if (empty($type)) {
144 // Note: search/X can not be a default tab because it would take on the
145 // path of its parent (search). It would prevent remembering keywords when
146 // switching tabs. This is why we drupal_goto to it from the parent instead.
147 drupal_goto('search/apachesolr_search');
148 }
149 $keys = trim(search_get_keys());
150 $filters = '';
151 if ($type == 'apachesolr_search' && isset($_GET['filters'])) {
152 $filters = trim($_GET['filters']);
153 }
154 // Only perform search if there is non-whitespace search term or filters:
155 if ($keys || $filters) {
156 // Log the search keys:
157 $log = $keys;
158 if ($filters) {
159 $log .= 'filters='. $filters;
160 }
161 watchdog('search', '%keys (@type).', array('%keys' => $log, '@type' => t('Search')), WATCHDOG_NOTICE, l(t('results'), 'search/'. $type .'/'. $keys));
162
163 // Collect the search results:
164 $results = search_data($keys, $type);
165
166 if ($results) {
167 $results = theme('box', t('Search results'), $results);
168 }
169 else {
170 $results = theme('box', t('Your search yielded no results'), search_help('search#noresults', drupal_help_arg()));
171 }
172 }
173 }
174 // Construct the search form.
175 return drupal_get_form('search_form', NULL, $keys, $type) . $results;
176 }
177
178 /**
179 * Execute a search results based on keyword, filter, and sort strings.
180 *
181 * @throws Exception
182 */
183 function apachesolr_search_execute($keys, $filters, $solrsort, $base_path = '', $page = 0, $caller = 'apachesolr_search') {
184 // Validate sort parameter
185 // TODO: move this to the query class.
186 if (strlen($solrsort) && preg_match('/^([a-z0-9_]+ (asc|desc)(,)?)+$/i', $solrsort)) {
187 $params['sort'] = $solrsort;
188 }
189 else {
190 $solrsort = '';
191 $params = array();
192 }
193 // This is the object that knows about the query coming from the user.
194 $query = apachesolr_drupal_query($keys, $filters, $solrsort, $base_path);
195 if (empty($query)) {
196 throw new Exception(t('Could not construct a Solr query in function apachesolr_search_search()'));
197 }
198 // This is the object that does the communication with the solr server.
199 $solr = apachesolr_get_solr();
200 $params += apachesolr_search_basic_params($query);
201 if ($keys) {
202 $params += apachesolr_search_highlighting_params($query);
203 $params += apachesolr_search_spellcheck_params($query);
204 }
205 else {
206 // No highlighting, use the teaser as a snippet.
207 $params['fl'] .= ',teaser';
208 }
209 apachesolr_search_add_facet_params($params, $query);
210 apachesolr_search_add_boost_params($params, $query, $solr);
211
212 $params['start'] = $page * $params['rows'];
213
214 // Cache the built query. Since all the built queries go through
215 // this process, all the hook_invocations will happen later
216 apachesolr_current_query($query);
217
218 // This hook allows modules to modify the query and params objects.
219 apachesolr_modify_query($query, $params, $caller);
220 if (!$query) {
221 return array();
222 }
223
224 if ('' == $keys) {
225 // Move the fq params to the q.alt for better performance.
226 $params['q.alt'] = implode(' ', $params['fq']);
227 unset($params['fq']);
228 }
229
230 $response = $solr->search($query->get_query_basic(), $params['start'], $params['rows'], $params);
231 // The response is cached so that it is accessible to the blocks and anything
232 // else that needs it beyond the initial search.
233 apachesolr_static_response_cache($response);
234 apachesolr_has_searched(TRUE);
235 // Add search terms and filters onto the breadcrumb.
236 drupal_set_breadcrumb(array_merge(menu_get_active_breadcrumb(), $query->get_breadcrumb()));
237 return apachesolr_process_response($response, $query, $params);
238 }
239
240 function apachesolr_search_basic_params($query) {
241 $params = array(
242 'fl' => 'id,nid,title,comment_count,type,created,changed,score,path,url,uid,name',
243 'rows' => variable_get('apachesolr_rows', 10),
244 'facet' => 'true',
245 'facet.mincount' => 1,
246 'facet.sort' => 'true'
247 );
248 return $params;
249 }
250
251 /**
252 * Add highlighting settings to the search params.
253 *
254 * These settings are set in solrconfig.xml.
255 * See the defaults there.
256 * If you wish to override them, you can via settings.php
257 */
258 function apachesolr_search_highlighting_params($query) {
259 $params['hl'] = variable_get('apachesolr_hl_active', NULL);
260 $params['hl.fragsize']= variable_get('apachesolr_hl_textsnippetlength', NULL);
261 $params['hl.simple.pre'] = variable_get('apachesolr_hl_pretag', NULL);
262 $params['hl.simple.post'] = variable_get('apachesolr_hl_posttag', NULL);
263 $params['hl.snippets'] = variable_get('apachesolr_hl_numsnippets', NULL);
264 $params['hl.fl'] = variable_get('apachesolr_hl_fieldtohightlight', NULL);
265 return $params;
266 }
267
268 function apachesolr_search_spellcheck_params($query) {
269 $params = array();
270 if (variable_get('apachesolr_search_spellcheck', FALSE)) {
271 //Add new parameter to the search request
272 $params['spellcheck.q'] = $query->get_query_basic();
273 $params['spellcheck'] = 'true';
274 }
275 return $params;
276 }
277
278 function apachesolr_search_add_facet_params(&$params, $query) {
279 $facet_query_limits = variable_get('apachesolr_facet_query_limits', array());
280 $facet_missing = variable_get('apachesolr_facet_missing', array());
281
282 foreach (apachesolr_get_enabled_facets() as $module => $module_facets) {
283 foreach($module_facets as $delta => $facet_field) {
284 // TODO: generalize handling of date and range facets.
285 if ($module == 'apachesolr_search' && ($facet_field == 'created' || $facet_field == 'changed')) {
286 list($start, $end, $gap) = apachesolr_search_date_range($query, $facet_field);
287 if ($gap) {
288 $params['facet.date'][] = $facet_field;
289 $params['f.'. $facet_field .'.facet.date.start'] = $start;
290 $params['f.'. $facet_field .'.facet.date.end'] = $end;
291 $params['f.'. $facet_field .'.facet.date.gap'] = $gap;
292 }
293 }
294 else {
295 $params['facet.field'][] = $facet_field;
296 // Facet limits
297 if (isset($facet_query_limits[$module][$delta])) {
298 $params['f.' . $facet_field . '.facet.limit'] = $facet_query_limits[$module][$delta];
299 }
300 // Facet missing
301 if (!empty($facet_missing[$module][$delta])) {
302 $params['f.' . $facet_field . '.facet.missing'] = 'true';
303 }
304 }
305 }
306 }
307
308 if (!empty($params['facet.field'])) {
309 // Add a default limit for fields where no limit was set.
310 $params['facet.limit'] = variable_get('apachesolr_facet_query_limit_default', 20);
311 }
312 }
313
314 function apachesolr_search_add_boost_params(&$params, $query, $solr) {
315 // Note - we have query fields set in solrconfig.xml, which will operate when
316 // none are set.
317 $qf = variable_get('apachesolr_search_query_fields', array());
318 $fields = $solr->getFields();
319 if ($qf && $fields) {
320 foreach ($fields as $field_name => $field) {
321 if (!empty($qf[$field_name])) {
322 if ($field_name == 'body') {
323 // Body is the only normed field.
324 $qf[$field_name] *= 40.0;
325 }
326 $params['qf'][] = $field_name . '^'. $qf[$field_name];
327 }
328 }
329 }
330
331 $data = $solr->getLuke();
332 if (isset($data->index->numDocs)) {
333 $total = $data->index->numDocs;
334 }
335 else {
336 $total = db_result(db_query("SELECT COUNT(nid) FROM {node}"));
337 }
338 // For the boost functions for the created timestamp, etc we use the
339 // standard date-biasing function, as suggested (but steeper) at
340 // http://wiki.apache.org/solr/DisMaxRequestHandler
341 // rord() returns 1 for the newset doc, and the number in the index for
342 // the oldest doc. The function is thus: $total/(rord()*$steepness + $total).
343 $date_settings = variable_get('apachesolr_search_date_boost', '4:200.0');
344 list($date_steepness, $date_boost) = explode(':', $date_settings);
345 if ($date_boost) {
346 $params['bf'][] = "recip(rord(created),$date_steepness,$total,$total)^$date_boost";
347 }
348 // Boost on comment count.
349 $comment_settings = variable_get('apachesolr_search_comment_boost', '0:0');
350 list($comment_steepness, $comment_boost) = explode(':', $comment_settings);
351 if ($comment_boost) {
352 $params['bf'][] = "recip(rord(comment_count),$comment_steepness,$total,$total)^$comment_boost";
353 }
354 // Boost for a more recent comment or node edit.
355 $changed_settings = variable_get('apachesolr_search_changed_boost', '0:0');
356 list($changed_steepness, $changed_boost) = explode(':', $changed_settings);
357 if ($changed_boost) {
358 $params['bf'][] = "recip(rord(last_comment_or_change),$changed_steepness,$total,$total)^$changed_boost";
359 }
360 // Boost for nodes with sticky bit set.
361 $sticky_boost = variable_get('apachesolr_search_sticky_boost', 0);
362 if ($sticky_boost) {
363 $params['bq'][] = "sticky:true^$sticky_boost";
364 }
365 // Boost for nodes with promoted bit set.
366 $promote_boost = variable_get('apachesolr_search_promote_boost', 0);
367 if ($promote_boost) {
368 $params['bq'][] = "promote:true^$promote_boost";
369 }
370 // Modify the weight of results according to the node types.
371 $type_boosts = variable_get('apachesolr_search_type_boosts', array());
372 if (!empty($type_boosts)) {
373 foreach ($type_boosts as $type => $boost) {
374 // Only add a param if the boost is != 0 (i.e. > "Normal").
375 if ($boost) {
376 $params['bq'][] = "type:$type^$boost";
377 }
378 }
379 }
380 }
381
382 function apachesolr_process_response($response, $query, $params) {
383 $results = array();
384 // We default to getting snippets from the body.
385 $hl_fl = is_null($params['hl.fl']) ? 'body' : $params['hl.fl'];
386 $total = $response->response->numFound;
387 pager_query("SELECT %d", $params['rows'], 0, NULL, $total);
388 if ($total > 0) {
389 foreach ($response->response->docs as $doc) {
390 $extra = array();
391
392 // Find the nicest available snippet.
393 if (isset($response->highlighting->{$doc->id}->$hl_fl)) {
394 $snippet = theme('apachesolr_search_snippets', $doc, $response->highlighting->{$doc->id}->$hl_fl);
395 }
396 elseif (isset($doc->teaser)) {
397 $snippet = theme('apachesolr_search_snippets', $doc, array(truncate_utf8($doc->teaser, 256, TRUE)));
398 }
399 else {
400 $snippet = '';
401 }
402
403 if (!isset($doc->body)) {
404 $doc->body = $snippet;
405 }
406 $doc->created = strtotime($doc->created);
407 $doc->changed = strtotime($doc->changed);
408 // Allow modules to alter each document.
409 drupal_alter('apachesolr_search_result', $doc);
410 // Copy code from comment_nodeapi().
411 $extra[] = format_plural($doc->comment_count, '1 comment', '@count comments');
412 $results[] = array(
413 'link' => url($doc->path),
414 'type' => apachesolr_search_get_type($doc->type),
415 'title' => $doc->title,
416 'user' => theme('username', $doc),
417 'date' => $doc->created,
418 'node' => $doc,
419 'extra' => $extra,
420 'score' => $doc->score,
421 'snippet' => $snippet,
422 );
423 }
424
425 // Hook to allow modifications of the retrieved results
426 foreach (module_implements('apachesolr_process_results') as $module) {
427 $function = $module .'_apachesolr_process_results';
428 $function($results);
429 }
430 }
431 return $results;
432 }
433
434 function apachesolr_search_date_range($query, $facet_field) {
435 foreach ($query->get_filters($facet_field) as $filter) {
436 // If we had an ISO date library we could use ISO dates
437 // directly. Instead, we convert to Unix timestamps for comparison.
438 // Only use dates if we are able to parse into timestamps.
439 $start = strtotime($filter['#start']);
440 $end = strtotime($filter['#end']);
441 if ($start && $end && ($start < $end)) {
442 $start_iso = $filter['#start'];
443 $end_iso = $filter['#end'];
444 // Determine the drilldown gap for this range.
445 $gap = apachesolr_date_gap_drilldown(apachesolr_date_find_query_gap($start_iso, $end_iso));
446 }
447 }
448 // If there is no $delta field in query object, get initial
449 // facet.date.* params from the DB and determine the best search
450 // gap to use. This callback assumes $delta is 'changed' or 'created'.
451 if (!isset($start_iso)) {
452 $start_iso = apachesolr_date_iso(db_result(db_query("SELECT MIN($facet_field) FROM {node} WHERE status = 1")));
453 $end_iso = apachesolr_date_iso(db_result(db_query("SELECT MAX($facet_field) FROM {node} WHERE status = 1")));
454 $gap = apachesolr_date_determine_gap($start_iso, $end_iso);
455 }
456 // Return a query range from the beginning of a gap period to the beginning
457 // of the next gap period. We ALWAYS generate query ranges of this form
458 // and the apachesolr_date_*() helper functions require it.
459 return array("$start_iso/$gap", "$end_iso+1$gap/$gap", "+1$gap");
460 }
461
462 /**
463 * Implementation of hook_apachesolr_facets().
464 *
465 * Returns an array keyed by block delta.
466 */
467 function apachesolr_search_apachesolr_facets() {
468 $facets = array();
469
470 $facets['type'] = array(
471 'info' => t('Apache Solr Search: Filter by content type'),
472 'facet_field' => 'type',
473 );
474 $facets['uid'] = array(
475 'info' => t('Apache Solr Search: Filter by author'),
476 'facet_field' => 'uid',
477 );
478 $facets['language'] = array(
479 'info' => t('Apache Solr Search: Filter by language'),
480 'facet_field' => 'language',
481 );
482
483 $facets['changed'] = array(
484 'info' => t('Apache Solr Search: Filter by updated date'),
485 'facet_field' => 'changed',
486 );
487 $facets['created'] = array(
488 'info' => t('Apache Solr Search: Filter by post date'),
489 'facet_field' => 'created',
490 );
491
492 // A book module facet.
493 if (module_exists('book')) {
494 $facets['is_book_bid'] = array(
495 'info' => t('Apache Solr Search: Filter by Book'),
496 'facet_field' => 'is_book_bid',
497 );
498 }
499
500 // Get taxonomy vocabulary facets.
501 if (module_exists('taxonomy')) {
502 $vocabs = taxonomy_get_vocabularies();
503 foreach ($vocabs as $vid => $vocab) {
504 // In this case the delta and facet field are the same.
505 $delta = 'im_vid_' . $vid;
506 $facets[$delta] = array(
507 'info' => t('Apache Solr Search: Filter by taxonomy @name', array('@name' => $vocab->name)),
508 'facet_field' => $delta,
509 );
510 }
511 }
512
513 // Get CCK field facets.
514 $fields = apachesolr_cck_fields();
515 if ($fields) {
516 foreach ($fields as $name => $field) {
517 // $delta can only be 32 chars, and the CCK field name may be this
518 // long also, so we cannot add anything to it.
519 $facets[$field['field_name']] = array(
520 'info' => t('Apache Solr Search: Filter by @field', array('@field' => $field['label'])),
521 'facet_field' => apachesolr_index_key($field),
522 );
523 }
524 }
525 return $facets;
526 }
527
528 /**
529 * Implementation of hook_block().
530 */
531 function apachesolr_search_block($op = 'list', $delta = 0, $edit = array()) {
532
533 switch ($op) {
534 case 'list':
535 $enabled_facets = apachesolr_get_enabled_facets('apachesolr_search');
536 $facets = apachesolr_search_apachesolr_facets();
537 // Add the blocks
538 $blocks = array();
539 foreach ($enabled_facets as $delta => $facet_field) {
540 if (isset($facets[$delta])) {
541 $blocks[$delta] = $facets[$delta] + array('cache' => BLOCK_CACHE_PER_PAGE,);
542 }
543 }
544 $blocks['currentsearch'] = array(
545 'info' => t('Apache Solr Search: Current search'),
546 'cache' => BLOCK_CACHE_PER_PAGE,
547 );
548 return $blocks;
549
550 case 'view':
551 if (apachesolr_has_searched()) {
552 // Get the query and response. Without these no blocks make sense.
553 $response = apachesolr_static_response_cache();
554 if (empty($response)) {
555 return;
556 }
557 $query = apachesolr_current_query();
558
559 $facets = apachesolr_get_enabled_facets('apachesolr_search');
560 if (empty($facets[$delta]) && ($delta != 'currentsearch')) {
561 return;
562 }
563
564 // Get information needed by the taxonomy blocks about limits.
565 $initial_limits = variable_get('apachesolr_facet_query_initial_limits', array());
566 $limit_default = variable_get('apachesolr_facet_query_initial_limit_default', 10);
567
568 // Handle taxonomy vocabulary facets
569 if ((strpos($delta, 'im_vid_') === 0) && module_exists('taxonomy')) {
570
571 if (is_object($response->facet_counts->facet_fields->$delta)) {
572 $contains_active = FALSE;
573 $terms = array();
574
575 foreach ($response->facet_counts->facet_fields->$delta as $tid => $count) {
576 $options = array();
577 if ($tid == '_empty_') {
578 // TODO - for now we don't handle facet missing.
579 continue;
580 }
581 $unclick_link = '';
582 unset($active);
583 $term = taxonomy_get_term($tid);
584 $new_query = clone $query;
585 if ($active = $query->has_filter('tid', $tid)) {
586 $contains_active = TRUE;
587 $new_query->remove_filter('tid', $term->tid);
588 $options['query'] = $new_query->get_url_querystring();
589 $link = theme('apachesolr_unclick_link', $term->name, $new_query->get_path(), $options);
590 }
591 else {
592 $new_query->add_filter('tid', $term->tid);
593 $options['query'] = $new_query->get_url_querystring();
594 $link = theme('apachesolr_facet_link', $term->name, $new_query->get_path(), $options, $count, $active, $response->response->numFound);
595 }
596 $countsort = $count == 0 ? '' : 1 / $count;
597 // if numdocs == 1 and !active, don't add.
598 if ($response->response->numFound > 1 || $active) {
599 $terms[$term->vid][$active ? $countsort . $term->name : 1 + $countsort . $term->name] = $link;
600 }
601 }
602 }
603 $vid = substr($delta, 7);
604 $vocab = taxonomy_vocabulary_load($vid);
605 if (is_numeric($vid) && is_array($terms) && isset($terms[$vid]) && is_array($terms[$vid])) {
606 ksort($terms[$vid]);
607 $limit = isset($initial_limits['apachesolr_search'][$delta]) ? $initial_limits['apachesolr_search'][$delta] : $limit_default;
608 return array(
609 'subject' => t('Filter by @name', array('@name' => $vocab->name)),
610 'content' => theme('apachesolr_facet_list', $terms[$vid], $limit),
611 );
612 }
613 return;
614 }
615
616 switch ($delta) {
617 case 'currentsearch':
618 $fields = $query->get_filters();
619 $path = $query->get_path();
620 $options = array();
621 if (!$fields) {
622 $options['attributes']['class'] = 'active';
623 }
624 $links[] = apachesolr_l($query->get_query_basic(), $path, $options);
625 foreach($fields as $field) {
626 if ($field['#name']) {
627 $new_query = clone $query;
628 $new_query->remove_filter($field['#name'], $field['#value']);
629 $options['query'] = $new_query->get_url_querystring();
630 $fielddisplay = theme("apachesolr_breadcrumb_". $field['#name'], $field['#value']);
631 if (!$fielddisplay) {
632 $fielddisplay = $field['#value'];
633 }
634 $links[] = theme('apachesolr_unclick_link', $fielddisplay, $new_query->get_path(), $options);
635 }
636 }
637 $content = theme('apachesolr_currentsearch', $response->response->numFound, $links);
638 return array('subject' => t('Current search'), 'content' => $content);
639 case 'is_book_bid':
640 return apachesolr_facet_block($response, $query, 'apachesolr_search', $delta, $delta, t('Filter by book'), 'apachesolr_search_get_book');
641 case 'language':
642 return apachesolr_facet_block($response, $query, 'apachesolr_search', $delta, $delta, t('Filter by language'), 'locale_language_name');
643 case 'uid':
644 return apachesolr_facet_block($response, $query, 'apachesolr_search', $delta, $delta, t('Filter by author'), 'apachesolr_search_get_username');
645 case 'type':
646 return apachesolr_facet_block($response, $query, 'apachesolr_search', $delta, $delta, t('Filter by type'), 'apachesolr_search_get_type');
647 case 'changed':
648 return apachesolr_date_facet_block($response, $query, 'apachesolr_search', $delta, $delta, t('Filter by modification date'));
649 case 'created':
650 return apachesolr_date_facet_block($response, $query, 'apachesolr_search', $delta, $delta, t('Filter by post date'));
651
652 default:
653 if ($fields = apachesolr_cck_fields()) {
654 foreach ($fields as $name => $field) {
655 if ($field['field_name'] == $delta) {
656 $index_key = apachesolr_index_key($field);
657 return apachesolr_facet_block($response, $query, 'apachesolr_search', $delta, $index_key, t('Filter by @field', array('@field' => $field['label'])));
658 }
659 }
660 }
661 }
662 break;
663 }
664 break;
665
666 case 'configure':
667 if ($delta != 'currentsearch') {
668 return apachesolr_facetcount_form('apachesolr_search', $delta);
669 }
670 break;
671
672 case 'save':
673 if ($delta != 'currentsearch') {
674 apachesolr_facetcount_save($edit);
675 }
676 break;
677 }
678 }
679
680 /**
681 * Callback function for the 'Filter by book' facet block.
682 */
683 function apachesolr_search_get_book($facet, &$options) {
684 if (is_numeric($facet)) {
685 return db_result(db_query('SELECT title FROM {node} WHERE nid = %d', $facet));
686 }
687 else {
688 $options['html'] = TRUE;
689 return theme('placeholder', t('Not in any book'));
690 }
691 }
692
693 /**
694 * Callback function for the 'Filter by name' facet block.
695 */
696 function apachesolr_search_get_username($facet) {
697 if (is_numeric($facet)) {
698 return theme('apachesolr_breadcrumb_uid', $facet);
699 }
700 return '';
701 }
702
703 /**
704 * Callback function for the 'Filter by type' facet block.
705 */
706 function apachesolr_search_get_type($facet) {
707 $type = node_get_types('name', $facet);
708 // A disabled or missing node type returns FALSE.
709 return ($type === FALSE) ? $facet : $type;
710 }
711
712 /**
713 * Implementation of hook_form_[form_id]_alter().
714 *
715 * This adds the 0 option to the search admin form.
716 */
717 function apachesolr_search_form_search_admin_settings_alter(&$form, $form_state) {
718 $form['indexing_throttle']['search_cron_limit']['#options']['0'] = '0';
719 ksort($form['indexing_throttle']['search_cron_limit']['#options']);
720 }
721
722 /**
723 * Implementation of hook_form_[form_id]_alter().
724 */
725 function apachesolr_search_form_search_theme_form_alter(&$form, $form_state) {
726 apachesolr_search_form_search_block_form_alter($form, $form_state);
727 }
728
729 /**
730 * Implementation of hook_form_[form_id]_alter().
731 */
732 function apachesolr_search_form_search_block_form_alter(&$form, $form_state) {
733 if (variable_get('apachesolr_search_make_default', 0)) {
734 if (!isset($form['#submit'])) {
735 $form['#submit'] = array('apachesolr_search_search_box_form_submit');
736 }
737 else {
738 $key = array_search('search_box_form_submit', $form['#submit']);
739 if ($key !== FALSE) {
740 // Replace the search module's function.
741 $form['#submit'][$key] = 'apachesolr_search_search_box_form_submit';
742 }
743 }
744 }
745 }
746
747 /**
748 * Process a block search form submission.
749 */
750 function apachesolr_search_search_box_form_submit($form, &$form_state) {
751 $form_id = $form['form_id']['#value'];
752 $keys = $form_state['values'][$form_id];
753 // Handle Apache webserver clean URL quirks.
754 if (variable_get('clean_url', '0')) {
755 $keys = str_replace('+', '%2B', $keys);
756 }
757 $form_state['redirect'] = 'search/apachesolr_search/'. trim($keys);
758 }
759
760 /**
761 * Implementation of hook_form_[form_id]_alter().
762 *
763 * This adds spelling suggestions, retain filters to the search form.
764 */
765 function apachesolr_search_form_search_form_alter(&$form, $form_state) {
766
767 if ($form['module']['#value'] == 'apachesolr_search') {
768 $filters = isset($_REQUEST['filters']) ? trim($_REQUEST['filters']) : '';
769 $form['basic']['apachesolr_search']['#tree'] = TRUE;
770 if ($filters) {
771 $form['basic']['apachesolr_search']['retain-filters'] = array(
772 '#type' => 'checkbox',
773 '#title' => t('Retain current filters'),
774 '#default_value' => !empty($_GET['retain-filters']),
775 );
776 }
777 $form['filters'] = array(
778 '#type' => 'hidden',
779 '#default_value' => $filters,
780 );
781
782 $form['#submit'] = array('apachesolr_search_form_search_submit');
783 if (variable_get('apachesolr_search_spellcheck', FALSE) && apachesolr_has_searched() && ($response = apachesolr_static_response_cache())) {
784 //Get spellchecker suggestions into an array.
785 $suggestions = get_object_vars($response->spellcheck->suggestions);
786
787 if ($suggestions) {
788 //Get the original query and replace words.
789 $query = apachesolr_current_query();
790
791 foreach($suggestions as $word => $value) {
792 $replacements[$word] = $value->suggestion[0];
793 }
794 $new_keywords = strtr($query->get_query_basic(), $replacements);
795
796 $form['basic']['suggestion'] = array(
797 '#prefix' => '<div class="spelling-suggestions">',
798 '#suffix' => '</div>',
799 '#type' => 'item',
800 '#title' => t('Did you mean'),
801 '#value' => l($new_keywords, $query->get_path($new_keywords)),
802 );
803 }
804 }
805 }
806 }
807
808 /**
809 * Added form submit function to account for Apache mode_rewrite quirks.
810 *
811 * @see apachesolr_search_form_search_form_alter()
812 */
813 function apachesolr_search_form_search_submit($form, &$form_state) {
814 $fv = $form_state['values'];
815 $keys = $fv['processed_keys'];
816 $query = '';
817 $base = 'search/'. $fv['module'] . '/';
818 if (variable_get('clean_url', '0')) {
819 $keys = str_replace('+', '%2B', $keys);
820 }
821 if (!empty($fv['apachesolr_search']['retain-filters']) && $fv['filters']) {
822 $query = 'filters=' . rawurlencode($fv['filters']) . '&retain-filters=1';
823 }
824 $form_state['redirect'] = array($base . $keys, $query);
825 if ($keys == '' && $query == '') {
826 form_set_error('keys', t('Please enter some keywords.'));
827 }
828 }
829
830 /**
831 * Implementation of hook_form_[form_id]_alter().
832 *
833 * This adds options to the apachesolr admin form.
834 */
835 function apachesolr_search_form_apachesolr_settings_alter(&$form, $form_state) {
836 $form['advanced']['apachesolr_search_make_default'] = array(
837 '#type' => 'radios',
838 '#title' => t('Make Apache Solr Search the default'),
839 '#default_value' => variable_get('apachesolr_search_make_default', 0),
840 '#options' => array(0 => t('Disabled'), 1 => t('Enabled')),
841 '#description' => t('Hides core node search, and makes the search block submit to Apache Solr Search'),
842 );
843 $form['advanced']['apachesolr_search_default_previous'] = array(
844 '#type' => 'value',
845 '#value' => variable_get('apachesolr_search_make_default', 0),
846 );
847 $form['apachesolr_search_spellcheck'] = array(
848 '#type' => 'checkbox',
849 '#title' => t('Enable spellchecker and suggestions'),
850 '#default_value' => variable_get('apachesolr_search_spellcheck', FALSE),
851 '#description' => t('Enable spellchecker and get word suggestions. Also known as the "Did you mean ... ?" feature.'),
852 );
853
854 $form['#submit'][] = 'apachesolr_search_build_spellcheck';
855 $form['#submit'][] = 'apachesolr_search_make_default_submit';
856 // Move buttons to the bottom.
857 $buttons = $form['buttons'];
858 unset($form['buttons']);
859 $form['buttons'] = $buttons;
860 }
861
862 /**
863 * Form submit funtion - do a menu rebuild if needed.
864 *
865 * @see apachesolr_search_form_apachesolr_settings_alter()
866 */
867 function apachesolr_search_make_default_submit($form, &$form_state) {
868 if ($form_state['values']['apachesolr_search_default_previous'] != variable_get('apachesolr_search_make_default', 0)) {
869 // Take account of path changes
870 menu_rebuild();
871 }
872 }
873
874 /**
875 * Implementation of hook_form_[form_id]_alter().
876 *
877 * Rebuild (empty) the spellcheck dictionary when the index is deleted..
878 */
879 function apachesolr_search_form_apachesolr_delete_index_form_alter(&$form, $form_state) {
880 $form['submit']['#submit'][] = 'apachesolr_search_build_spellcheck';
881 }
882
883 function apachesolr_search_build_spellcheck() {
884 try {
885 $solr = apachesolr_get_solr();
886 $params['spellcheck'] = 'true';
887 $params['spellcheck.build'] = 'true';
888 $response = $solr->search('solr', 0, 0, $params);
889 }
890 catch (Exception $e) {
891 watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
892 }
893 }
894
895 /**
896 * Implementation of hook_theme().
897 */
898 function apachesolr_search_theme() {
899 return array(
900 'apachesolr_breadcrumb_is_book_bid' => array(
901 'arguments' => array('bid' => NULL),
902 ),
903 'apachesolr_breadcrumb_uid' => array(
904 'arguments' => array('uid' => NULL),
905 ),
906 'apachesolr_breadcrumb_tid' => array(
907 'arguments' => array('tid' => NULL),
908 ),
909 'apachesolr_breadcrumb_type' => array(
910 'arguments' => array('type' => NULL),
911 ),
912 'apachesolr_breadcrumb_changed' => array(
913 'arguments' => array('type' => NULL),
914 ),
915 'apachesolr_breadcrumb_created' => array(
916 'arguments' => array('type' => NULL),
917 ),
918 'apachesolr_currentsearch' => array(
919 'arguments' => array('total_found' => NULL, 'links' => NULL),
920 ),
921 'apachesolr_search_snippets' => array(
922 'arguments' => array('doc' => NULL, 'snippets' => NULL),
923 ),
924 );
925 }
926
927 function theme_apachesolr_breadcrumb_date_range($range) {
928 if (preg_match('@[\[\{](\S+) TO (\S+)[\]\}]@', $range, $match)) {
929 return apachesolr_date_format_range($match[1], $match[2]);
930 }
931 return $range;
932 }
933
934 function theme_apachesolr_breadcrumb_changed($range) {
935 return theme_apachesolr_breadcrumb_date_range($range);
936 }
937
938 function theme_apachesolr_breadcrumb_created($range) {
939 return theme_apachesolr_breadcrumb_date_range($range);
940 }
941
942 /**
943 * Return the username from $uid
944 */
945 function theme_apachesolr_breadcrumb_uid($uid) {
946 if ($uid == 0) {
947 return variable_get('anonymous', t('Anonymous'));
948 }
949 else {
950 return db_result(db_query("SELECT name FROM {users} WHERE uid = %d", $uid));
951 }
952 }
953
954 /**
955 * Return the term name from $tid.
956 */
957 function theme_apachesolr_breadcrumb_tid($tid) {
958 $term = taxonomy_get_term($tid);
959 return $term->name;
960 }
961
962 /**
963 * Return the human readable text for a content type.
964 */
965 function theme_apachesolr_breadcrumb_type($type) {
966 return node_get_types('name', $type);
967 }
968
969 /**
970 * Return the title of a book.
971 */
972 function theme_apachesolr_breadcrumb_is_book_bid($bid) {
973 if (is_numeric($bid)) {
974 return db_result(db_query('SELECT title FROM {node} WHERE nid = %d', $bid));
975 }
976 else {
977 return t('Not in any book');
978 }
979 }
980
981 /**
982 * Return current search block contents
983 */
984 function theme_apachesolr_currentsearch($total_found, $links) {
985 return theme_item_list($links, format_plural($total_found, 'Search found 1 item', 'Search found @count items'));
986 }
987
988 /**
989 * Theme the highlighted snippet text for a search entry.
990 *
991 * @param object $doc
992 * @param array $snippets
993 *
994 */
995 function theme_apachesolr_search_snippets($doc, $snippets) {
996 return implode(' ... ', $snippets) .' ...';
997 }
998

  ViewVC Help
Powered by ViewVC 1.1.2