/[drupal]/contributions/modules/semantic_search/includes/semantic_search.search.inc
ViewVC logotype

Contents of /contributions/modules/semantic_search/includes/semantic_search.search.inc

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


Revision 1.1 - (show annotations) (download) (as text)
Tue Jun 5 06:37:07 2007 UTC (2 years, 5 months ago) by hendler
Branch: MAIN
CVS Tags: HEAD
Branch point for: DRUPAL-5
File MIME type: text/x-php
adding files from our SVN
1 <?php
2 /*
3 * Copyright (C) October 2006 Jonathan Hendler
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
18 *
19 * The author can be contacted at:
20 * - jonathan [at] civicactions (dot) com
21 *
22 * @license http://www.affero.org/oagpl.html
23 * @link http://drupal.org/project/semantic_search
24 */
25 /**
26 * The Following search class is a collection of functions used in the SEMANTIC_SEARCH module.
27 * It provides the API for constructing search elements
28 *
29 *
30 * @todo should be more configurable for data including the search terms
31 * @author Jonathan Hendler
32 * @package SEMANTIC_SEARCH
33 *
34 * */
35 class NinaSearch {
36 /**
37 * the SONIA api search tool
38 * */
39 public $sonia;
40
41 /**
42 * semantic_search search params
43 * */
44 public $search_params;
45
46 /**
47 * controls if users are allowed to create, update or delete data
48 * */
49 private $allowCrud;
50
51 /**
52 * The constructor initiates a SONIA object
53 * */
54 function NinaSearch() {
55
56 //__nsp(' NinaSearch() start');
57 //construct SONIA (see sonia.conf.inc for constants)
58
59 $this->sonia = new SONIA(SONIA_BACKEND_TYPE, SONIA_FORM_ARRAY_PREFIX);
60
61 $this->search_hash = $this->getSearchHash();
62 // build search query
63 $this->search_params = $this->sonia->getSearchParams();
64
65 if ($this->doSearch()) {
66 $this->search_results = $this->sonia->getSearchResults();
67 $this->object_search_result_ids = $this->sonia->store->object_search_result_ids;
68 }
69
70 }
71
72 private function getSearchHash() {
73 //cache, at this leve needs to know if the page has been cached
74 // specific to pagination page
75 $pgnation = $this->sonia->io->getPaginationBegin();
76
77 if (empty ($this->sonia->sonia_search_array)) {
78 $key = 'empty';
79 } else {
80 $key = array ();
81 foreach ($this->sonia->sonia_search_array as $q) {
82 $key[$q['predicate']] = $q['subject'] . $q['object'];
83 }
84 }
85 return SO_CA :: gh($key, $pgnation, true);
86 }
87
88 /**
89 * used to capture params from search params
90 *
91 * note:
92 * uri must be not encoded
93 * */
94 function searchedForURI($uri, $type) {
95
96 if (is_array($this->search_params[$type])) {
97 foreach ($this->search_params[$type] as $kur => $asset) {
98 if (!is_array($asset)) {
99 if ($kur == $uri)
100 return true;
101 if ($asset == $uri)
102 return true;
103 } else {
104 foreach ($asset as $key => $value) {
105 if ($key == $uri && ($type != 'facet')) {
106 return true;
107 }
108 if ($value == $uri){
109 return true;
110 }
111 }
112 }
113 }
114 }
115 return false;
116 }
117
118 /**
119 * count the number of facets found in search
120 * */
121 function countInSearch($uri, $type,$predicate= null) {
122 if (empty ($this->search_results) || empty($this->object_search_result_ids))
123 return 0;
124 $count = 0;
125
126 if ($type == 'cat') {
127 return $this->sonia->store->getObjectsCountInSearch( $uri,SONIA_RDF_CREATE_TYPE, false, false);
128
129 } else
130 if ($type == 'facet') {
131 return $this->sonia->store->getObjectsCountInSearch(null,$uri);
132
133 } else
134 if ($type == 'p_value') {
135 return $this->sonia->store->getObjectsCountInSearch($uri,$predicate);
136 }
137
138 return $count;
139 }
140
141 /**
142 *
143 * */
144 public function getAllBaseOptions(){
145 $all_base_elements = $this->sonia->getBaseElementsAndCounts();
146 $uris = array();
147 $type_name = '';
148
149 foreach($all_base_elements as $type => $data){
150 $uris[] = strtoupper($type);
151 foreach($data as $uri =>$values) {
152 $uri = SONIA_H :: encodeURI($uri);
153 $uris[$uri] = '-'.$values['label'] . '('.$values['count'].')';
154 }
155 }
156
157 return $uris;
158 }
159
160 //---------------------------------------------------------------------------
161 /**
162 * The generalized function which allows those creating semantic_search.app.inc to build
163 * custom search elements.
164 *
165 * It's all about abstraction.
166 *
167 *
168 * @param array $form_element_options
169 * $form_element_options['#query_type'] = 'class', 'property', 'object','p_value',fulltext
170 * $form_element_options['uris'] = array of uris as top level elements
171 * $form_element_options['#type'] = 'checkbox','textfield','select', 'radio'
172 * $form_element_options['#multiple_facet'] = allow multiple facets to be searched on without overwriting
173 * $form_element_options['#return_type'] = 'list_objects' 'list_properties' 'list_values'
174 * $form_element_options['#title'] = $title title/label for the search item
175 * $form_element_options['#min'] = (value integero or alphbet to do telescoping range searches)
176 * $form_element_options['#max'] = (value integero or alphbet to do telescoping range searches)
177 * $form_element_options['#description'] = string $description description of the field
178 * $form_element_options['#sort_order'] = asc | desc
179 * $form_element_options['#order_by'] = alpha | count
180 * $form_element_options['#return_counts'] = boolean - return numbers
181 * $form_element_options['#grand_total'] = return total results (in select )
182 * $form_element_options['#title_as_option'] = title should not display as a label, but as an defaul option (in selects)
183 *
184 *
185 * @param string $order_by eg. 'label ASC'
186 * @param boolean $return_counts include count information, can be a performance hit
187 *
188 * @see SONIA :: getCustomSearchParamsData()
189 * @see SONIA :: getSearchParamsData()
190 *
191 * @todo add support for multiple field search instead of all or one on fulltext
192 * */
193 //---------------------------------------------------------------------------
194 public function getSearchFormElement($form_element_options) {
195 $result_array = $form_element = array ();
196 $prefix = '';
197
198 //defaults
199 if (!isset ($form_element_options['values']))
200 $form_element_options['values'] = null;
201 if (!isset ($form_element_options['#title']))
202 $form_element_options['#title'] = '';
203 if (!isset ($form_element_options['#description']))
204 $form_element_options['#description'] = '';
205 if (!isset ($form_element_options['#return_counts']))
206 $form_element_options['#return_counts'] = true;
207 if (!isset ($form_element_options['#grand_total']))
208 $form_element_options['#grand_total'] = true;
209 if (!isset ($form_element_options['#order_by']))
210 $form_element_options['#order_by'] = 'alpha';
211 if (!isset ($form_element_options['#sort_order']))
212 $form_element_options['#sort_order'] = 'asc';
213 if (!isset ($form_element_options['#multiple_facet']))
214 $form_element_options['#multiple_facet'] = false;
215 if (!isset ($form_element_options['#title_as_option']))
216 $form_element_options['#title_as_option'] = false;
217
218 if (!$form_element_options['#title_as_option']) {
219 $form_element['#title'] = $form_element_options['#title'];
220 $form_element['#description'] = $form_element_options['#description'];
221 }
222
223 $search_name = (isset($form_element_options['#search_name'])) ? '/'.$form_element_options['#search_name']: '';
224 $multi_f = $form_element_options['#multiple_facet'];
225 //check URIs
226 if (!is_array($form_element_options['uris']))
227 $form_element_options['uris'] = array (
228 $form_element_options['uris']
229 );
230 $uris = $form_element_options['uris'];
231 //what if a query involves custom classes or properties?
232 //for now assuming queries and classes don't need both
233
234
235 switch ($form_element_options['#query_type']) {
236 case 'class' :
237 //$form_element['#name'] = 'cat';
238 $prefix = 'cat';
239 break;
240 case 'property' :
241 $prefix = 'facet';
242 break;
243 case 'p_value' :
244 //works like text search becauase it's by values
245 $prefix = 'p_value';
246 break;
247 case 'object' :
248
249 break;
250 case 'fulltext' :
251 //todo
252 $rvalue = (isset ($uris[0])) ? SONIA_H :: encodeURI($uris[0]) : '';
253 $qname = (!$multi_f) ? 'qstring][' . $rvalue : 'qstring][][' . $rvalue;
254 $form_element[$qname]['#title'] = $form_element_options['#title'];
255 $form_element[$qname]['#description'] = $form_element_options['#description'];
256 $form_element[$qname]['#type'] = 'textfield';
257 $form_element[$qname]['#size'] = SEMANTIC_SEARCH_TEXT_INPUT_FORM_WIDTH;
258 return $form_element;
259 break;
260 default :
261 $message = $form_element_options['#query_type'] . ' is not a valid $query_type for getSearchFormElement()';
262 $this->sonia->io->throwError($message);
263 break;
264 }
265
266 //javascript?
267 //form_element['#attributes'] = '';
268
269 if ($form_element_options['#return_type'] == 'taxonomy_hack') {
270 $result_array = $this->sonia->taxonomyForm($form_element_options['values']);
271
272 } else {
273 $result_array = $this->sonia->getCustomSearchParamsData($form_element_options['#query_type'], $uris, $form_element_options['#return_type'], $form_element_options['values'], $form_element_options['#order_by'], $form_element_options['#return_counts']);
274 }
275 if (empty($result_array)){
276 $result_array = array();
277 }
278
279
280 switch ($form_element_options['#type']) {
281 case 'select' :
282 $array_prefix = null;
283 if ($prefix == 'p_value') { //TODO maybe others
284 $rvalue = SONIA_H :: encodeURI($uris[0]);
285 $array_prefix = (!$multi_f) ? "p_value][$rvalue" : "p_value][][$rvalue";
286 if (SEMANTIC_SEARCH_IS_DRUPAL_5)
287 $array_prefix = "edit[$array_prefix]";
288 }
289
290 if ($prefix == 'facet') { //TODO maybe others
291 //$rvalue = SONIA_H :: encodeURI($uris[0]);
292 $array_prefix = (!$multi_f) ? "facet][" : "facet][][";
293 if (SEMANTIC_SEARCH_IS_DRUPAL_5)
294 $array_prefix = "edit[$array_prefix]";
295 }
296
297 $tarray = array ();
298 $form_element['#type'] = $form_element_options['#type'];
299 //if js
300 if (true) {
301 $path = url('semantic_search_ajax/search'.$search_name, null, null, true);
302 $base_url = $this->makeQueryURL();
303 $html_id = md5(time() . $uris[0].print_r($form_element_options,1));
304 //
305 $url = $path . '?' . $base_url . '&'; //.SONIA_FORM_REQUEST_PREFIX.'['.$prefix.']'
306 //$jscall = " semantic_searchSearchSubmitSelect('" . $url . "', '" . $html_id . "','" . $prefix . "'); ";
307 // dbg($this->makefullJSRPCURL($search_name),'$this->makefullJSRPCURL()');
308 $jscall = " setUrlPrefix('" . $this->makefullJSRPCURL($search_name) . "'); semantic_searchSearchSubmitSelect( '" . $html_id . "','" . $prefix . "'); ";
309 $form_element['#attributes'] = array (
310 'onChange' => $jscall,
311 'id' => $html_id
312 );
313 }
314 $total_total = 0;
315 $total_inseach_total = 0;
316 $tarray[0] = '';
317
318 /* TODO should become a switch of choices about how to sort
319 * */
320 $alphasort = (empty ($form_element_options['#order_by']) || $form_element_options['#order_by'] != 'num');
321
322 $asc = (empty ($form_element_options['#sort_order']) || $form_element_options['#sort_order'] != 'desc');
323
324 if (!$alphasort) {
325 $count_array[0] = 800000;
326 }
327
328 $top_option = null;
329 foreach ($result_array as $uri => $data) {
330
331 $encuri = SONIA_H :: encodeURI($uri);
332 $total_total += $data['count'];
333 $predicate = ($prefix == 'p_value' )?$uris[0]:null;
334 $predicate = ($prefix == 'facet' )?$uri:null;
335
336 //NOTE: $multi_f will not show results in search since if you only can search on
337 // on field, then all the others will reflect 0 and not encourage userse
338 // to search elsewhere
339
340 //TODO if this wasn't selected
341 $not_active = false;
342 $num_in_search = ($this->doSearch() && !empty ($this->search_results) && ($multi_f || $not_active || isset($form_element_options['#show_in_search_count']))) ? $this->countInSearch($uri, $prefix,$predicate) : $data['count'];
343 $tarray[$encuri] = "{$data['label']} " . ' ' . "( $num_in_search)";
344 if (isset($form_element_options['#top_option']) && $form_element_options['#top_option'] == $data['label']){
345 $top_option = $encuri;
346 }
347
348 if (!$alphasort) {
349 $count_array[$encuri] = $num_in_search;
350 }
351 if ($this->searchedForURI($uri, $prefix) && $num_in_search>0) {
352 $form_element['#default_value'] = $encuri;
353 if ($multi_f)
354 $tarray[$encuri] .= ' ** ';
355 } else {
356
357 }
358 } //end foreach
359
360 //sort
361 if ($alphasort) {
362 if ($asc)
363 asort($tarray);
364 else
365 arsort($tarray);
366 } else { //number sort
367 if ($asc)
368 asort($count_array);
369 else
370 arsort($count_array);
371
372 $new_array = array ();
373 foreach ($count_array as $encuri => $_count) {
374 $new_array[$encuri] = $tarray[$encuri];
375 }
376 $tarray = $new_array;
377 }
378
379 //then add first option
380 if ($form_element_options['#title_as_option'] == true) {
381 $display_count = ($this->doSearch() && !empty ($this->search_results)) ? $total_inseach_total : $total_total;
382
383 $display_count = ($form_element_options['#grand_total'] == true) ? "($display_count)" : '';
384
385 $tarray[0] = "{$form_element_options['#title']} $display_count";
386 } else {
387 $tarray[0] = ' please select';
388 }
389
390 //re-situate top option
391 if ($top_option !== null) {
392 $new_array = array ();
393 $new_array[0] = $tarray[0];
394 $new_array[$top_option] = $tarray[$top_option];
395 //do the rest
396 foreach ($tarray as $encuri => $_count) {
397 if (!empty($encuri) && $encuri != $top_option){
398 $new_array[$encuri] = $tarray[$encuri];
399 }
400 }
401
402 $tarray = $new_array;
403 }
404
405 if ( $form_element_options['#uris_only']) {
406 return $tarray;
407 }
408
409 $form_element['#options'] = $tarray;
410 break;
411
412 case 'checkbox' : //assumes to be a group
413 $tarray = array ();
414 $count = 1;
415 if ($form_element_options['#title_as_option']) {
416 $form_element['#title'] = $form_element_options['#title'];
417 $form_element['#description'] = $form_element_options['#description'];
418 }
419 $form_element['#type'] = 'fieldset';
420 foreach ($result_array as $uri => $data) {
421 // a little switch
422 if ($prefix == 'p_value') {
423 $rvalue = SONIA_H :: encodeURI($uris[0]);
424 } else {
425 $rvalue = 1;
426 }
427
428 $encuri = SONIA_H :: encodeURI($uri);
429 $dv = $this->searchedForURI($uri, $prefix);
430
431 $form_element[$prefix . '][' . $encuri]['#title'] = "{$data['label']} ";
432 $num_in_search = ($this->doSearch() && !empty ($this->search_results)) ? $this->countInSearch($uri, $prefix) : $data['count'];
433 $form_element[$prefix . '][' . $encuri]['#title'] .= '<span class="countArea" >&nbsp;' . "($num_in_search)</span>";
434
435 $form_element[$prefix . '][' . $encuri]['#description'] = $form_element_options['#description'];
436 $form_element[$prefix . '][' . $encuri]['#type'] = $form_element_options['#type'];
437 //$form_element[$uri]['#value'] = $uri;
438 $form_element[$prefix . '][' . $encuri]['#default_value'] = $dv;
439 $form_element[$prefix . '][' . $encuri]['#return_value'] = $rvalue;
440 $count++;
441 }
442 break;
443
444 case 'links' :
445
446 $items = array ();
447 if (!$alphasort) {
448 $count_array[0] = 800000;
449 }
450
451 if ($prefix == 'p_value' || $prefix == 'cat' ) {
452 $rvalue = SONIA_H :: encodeURI($uris[0]);
453 } else {
454 $rvalue = '';
455 }
456
457 //HACK for AHIRC, maybe there is a better solution
458 if ($form_element_options['#return_type'] == 'telescope'){
459 foreach($result_array as $result_arraytemp){
460 if (empty($result_arraytemp['label']) && !empty($result_arraytemp['children'])){
461 $result_array = $result_arraytemp['children'];
462 break;
463 }
464 }
465 }
466 $multi_search = (!$multi_f) ? '' : '[]';
467 $predicate = ($prefix == 'p_value'|| $prefix == 'facet' )?$uris[0]:null;
468
469 foreach ($result_array as $uri => $data) {
470 $title_temp = $data['label'];
471 $encuri = SONIA_H :: encodeURI($uri);
472 $dv = $this->searchedForURI($uri, $prefix);
473
474 $num_in_search = ($this->doSearch() && !empty ($this->search_results)) ? $this->countInSearch($uri, $prefix,$predicate) : $data['count'];
475
476 $show_counts = ($form_element_options['#return_type'] != 'telescope')
477 || ($form_element_options['#return_type'] == 'telescope' && (!isset ($data['children']) || !is_array($data['children'])));
478
479 $telescope_level_2 = (!$show_counts );
480
481 $counts = ($show_counts) ?'<span class="countArea" >&nbsp;' . "($num_in_search)</span>":'';
482 if (!$alphasort) {
483 $count_array[$encuri] = $num_in_search;
484 }
485
486 $desc = "for " . htmlentities($title_temp). ' in ' .$num_in_search . ' results ';
487
488 //if js enabled
489 //$show_counts && !isset ($data['children'] ) &&
490 if (!empty($encuri) && !$dv) {
491 $path = url('semantic_search_ajax/search'.$search_name, null, null, true);
492 $base_url = $this->makeQueryURL();
493 $url = $path . '?' . $base_url . '&' . SONIA_FORM_REQUEST_PREFIX . '[' . $prefix . ']' . $multi_search . '[' . $rvalue . ']=' . $encuri;
494
495 if ($show_counts){
496 $items[$encuri] = '<span class="semantic_searchSearchList" title="search: ' . $desc . '" onclick="semantic_searchSearchSubmitLink(\'' . $url . '\')" >' . $title_temp . ' ' . $counts . '</span>';
497 }
498 else {
499 $items[$encuri] = '<span class="semantic_searchSearchListNoSearch" >' . $title_temp . ' ' . $counts . '</span>';
500 }
501
502
503 }
504
505 // else {
506 // $base_url = $this->makefullURLforRedirect();
507 // $url = $base_url . '&' . SONIA_FORM_REQUEST_PREFIX . '[' . $prefix . ']' . $multi_search . '[' . $rvalue . ']=' . $encuri;
508 // $items[$encuri] = '<a href="' . $url . '" title="search: ' . $desc . '" >' . $title_temp . '</a>' . $counts;
509 // }
510
511 if ($dv && !empty($encuri)) {
512 $items[$encuri] = '<span class="semantic_searchListSearched" >' . $items[$encuri] . '</span>';
513 }
514
515 if (!empty($encuri) && $form_element_options['#return_type'] == 'telescope' && isset($data['description'])){
516 $items[$encuri] .= '<div class="link-description">'.$data['description'].'</div>';
517 }
518
519 //if this is a telescoping navigation, get the next layer
520 if ($form_element_options['#return_type'] == 'telescope') {
521 if (isset ($data['children']) && is_array($data['children'])) {
522 $path = url('semantic_search_ajax/search', null, null, true);
523 $base_url = $this->makeQueryURL();
524 $titems = array ();
525 $count_array2 = array ();
526
527 foreach ($data['children'] as $uri_2 => $tdata) {
528
529 $title_temp2 = $tdata['label'];
530 //if the title is empty ignore it.
531 if ($title_temp2 == '') continue;
532 $desc2 = "for " . $title_temp .' in ' .$title_temp2;
533 $encuri2 = SONIA_H :: encodeURI($uri_2);
534 $dv2 = $this->searchedForURI($uri_2, $prefix);
535 $multi_search = (!$multi_f) ? '' : '[]';
536 $num_in_search2 = ($this->doSearch() && !empty ($this->search_results)) ? $this->countInSearch($uri_2, $prefix) : $tdata['count'];
537 $counts2 = '<span class="countArea" >&nbsp;' . "($num_in_search2)</span>";
538 if (!$alphasort) {
539 $count_array2[$encuri2] = $num_in_search2;
540 }
541 $url2 = $path . '?' . $base_url . '&' . SONIA_FORM_REQUEST_PREFIX . '[' . $prefix . ']' . $multi_search . '[' . $rvalue . ']=' . $encuri2;
542 $titems[$encuri2] = '<span class="semantic_searchSearchList" title="search: ' . $desc2. '" onclick="semantic_searchSearchSubmitLink(\'' . $url2 . '\')" >' . $title_temp2 . ' ' . $counts2 . '</span>';
543 if ($dv2) {
544 $titems[$encuri2] = '<span class="semantic_searchListSearched" >' . $titems[$encuri2] . '</span>';
545 }
546 }
547 $this->sortItems($titems, $count_array2, $alphasort, $asc);
548 $items[$encuri] .= theme_item_list($titems);
549 }
550 }
551 }
552
553 $this->sortItems($items, $count_array, $alphasort, $asc);
554
555
556 $title = $form_element_options['#title'];
557 $description = $form_element_options['#description'];
558 if (!empty($title)){
559 $form_element['#type'] = 'fieldset';
560 $form_element['#title'] = $title;
561 $form_element['#description'] = $description;
562 }
563 $form_element['sub']['#value'] = theme('item_list',$items ,null,true);
564 break;
565
566 case '' :
567 break;
568
569 default :
570 $message = $form_element_options['#type'] . ' is not a supported $form_type in getSearchFormElement()';
571 $this->sonia->io->throwError($message);
572
573 }
574
575 if ($array_prefix !== null) { //set up for multiple
576 return array (
577 $array_prefix => $form_element
578 );
579 }
580 return $form_element;
581 }
582
583 /**
584 * helper function to generate layers of links
585 * */
586 private function createHieararchicalLinks(& $items, & $count_array, $title, $description, $alphasort, $asc) {
587
588 $form_element['#type'] = 'fieldset';
589 $form_element['#title'] = $title;
590 $form_element['#description'] = $description;
591 $form_element['sub']['#value'] = theme_item_list($items);
592 return $form_element;
593 }
594
595 /**
596 * helper function to sort item list
597 * */
598 private function sortItems(& $items, & $count_array, $alphasort, $asc) {
599 if ($alphasort) {
600 if ($asc)
601 asort($items);
602 else
603 arsort($items);
604 } else { //number sort
605 if ($asc)
606 asort($count_array);
607 else
608 arsort($count_array);
609
610 $new_array = array ();
611 foreach ($count_array as $encuri => $_count) {
612 $new_array[$encuri] = $items[$encuri];
613 }
614 $items = $new_array;
615 }
616 }
617
618 /**
619 * returns absolute path to the node in drupal
620 * */
621 public static function makeNodeURL($id, $label = 'node', $query = null) {
622 return url($label . '/' . $id, $query, null, true);
623 }
624
625 public function getClassProperties(& $count, $class) {
626 return $this->sonia->getAllClassProperties($count, $class);
627 }
628
629 //////////////////////////////////////////////
630 //UNFILTERED
631
632 public function getLabel($object) {
633 return $this->sonia->getLabel($object);
634 }
635
636 public function getClasses($object) {
637 return $this->sonia->getClasses($object);
638 }
639
640 /////////////////////////////////////////////////
641
642 public function passFormValues($form_values) {
643 $this->sonia->io->setRequest($form_values);
644 }
645
646 public function dataSubmitted() {
647 $this->sonia->_dataSubmitted();
648 }
649
650 /**
651 * wrapper to expose private function signalling that a search has commenced
652 * */
653 public function doSearch() {
654 return $this->sonia->io->doSearch();
655 }
656
657 /**
658 * needed because drupals forms perform a redirect from POST to GET.
659 * */
660 public function makefullURLforRedirect() {
661 $qstring = SONIA_URL_H :: makeFullURLFromSearch($this->search_params);
662 return url(SEMANTIC_SEARCH_BASE_URL, $qstring, null, true);
663 }
664
665 public function makefullJSRPCURL($search_name = '') {
666 $qstring = SONIA_URL_H :: makeFullURLFromSearch($this->search_params);
667 return SEMANTIC_SEARCH_JSRPC_PATH.$search_name.'?'. $qstring;
668 }
669
670 /**
671 *
672 * */
673 public function makeQueryURL() {
674 $qstring = SONIA_URL_H :: makeFullURLFromSearch($this->search_params);
675 return $qstring;
676 }
677
678 public function getSessionHash() {
679 return $this->sonia->getSessionHash();
680 }
681
682 /**
683 *
684 *
685 * @return boolean
686 */
687 public function searchResultsFound() {
688 return $this->doSearch() && is_array($this->search_results) && !empty ($this->search_results);
689
690 }
691
692 public function searchParamsActive() {
693 return $this->doSearch() && is_array($this->search_params) && !empty ($this->search_params);
694 }
695
696 /**
697 * get the data needed to create search result pagination elements in a theme
698 * */
699 public function searchPaginaton() {
700 $data = array ();
701 $data['base_url'] = $this->makefullURLforRedirect();
702 $data['restart_url'] = $this->getRestartSearchLink();
703 $data['do_search'] = $this->doSearch();
704 $data['search_results_success'] = $this->searchResultsFound();
705 $data['total'] = $this->sonia->store->object_search_result_count;
706 //needed?
707 $this->number_of_search_results = $data['total'];
708
709 $data['begin'] = $this->sonia->io->getPaginationBegin();
710 $data['end'] = ($data['total'] < (SEMANTIC_SEARCH_DISPLAY_PAGINATION + $data['begin'])) ? $data['total'] : (SEMANTIC_SEARCH_DISPLAY_PAGINATION + $data['begin']);
711
712 if ($data['begin'] >= SEMANTIC_SEARCH_DISPLAY_PAGINATION || (($data['begin'] + 1) > $data['total'])) {
713 $previous = (0 > ($data['begin'] - SEMANTIC_SEARCH_DISPLAY_PAGINATION)) ? 0 : $data['begin'] - SEMANTIC_SEARCH_DISPLAY_PAGINATION;
714 $data['previous_link'] = $data['base_url'] . '&' . SEMANTIC_SEARCH_PAGINATION_SIGNAL . '=' . $previous;
715 }
716
717 if ($data['end'] < $data['total']) {
718 $next = ($data['end'] < ($data['begin'] + SEMANTIC_SEARCH_DISPLAY_PAGINATION)) ? $data['end'] : $data['begin'] + SEMANTIC_SEARCH_DISPLAY_PAGINATION;
719 $data['next_link'] = $data['base_url'] . '&' . SEMANTIC_SEARCH_PAGINATION_SIGNAL . '=' . $next;
720 }
721
722 return $data;
723 }
724
725 /**
726 * JellyBeans are like breadcrumbs, but better.
727 *
728 * The function provides text based feedback on what the user is searching for
729 * The feedback is alos interactive - allowing them to add and remove sub-elements
730 * of the query.
731 *
732 *
733 * */
734 function jellyBean($not_rpc = true, $stand_alone_block = true, $extra = '') {
735 //data to return
736 $data = array ();
737 $data['results_found'] = $this->searchResultsFound();
738 $data['search_active'] = $this->searchParamsActive();
739 if (!$this->searchParamsActive())
740 return array ();
741
742 $data['is_rpc'] = !$not_rpc;
743 $data['url'] = NinaNav :: search_link($this->search_params);
744 $data['jellyBeanMenu'] = theme("semantic_search_navigation_menu", $this->jellyBeanMenu($data['url']));
745 $data['extra'] = $extra;
746 $data['jellyBeans'] = array (); //o key, 1 value, 3 remove link
747
748 $looking = false;
749 $url = NinaNav :: search_link($this->search_params);
750 $count = 0;
751
752 if ($this->searchParamsActive()) {
753
754 foreach ($this->search_params as $key => $value) {
755 //ignore placholder
756 if ($key === 0) {
757 continue;
758 }
759
760 if (!is_array($value)) {
761 //$html .= $value . ' $value ';
762 if ($value !== '') {
763
764 $data['jellyBeans'][] = $this->makeJellyBean($url, $key, $key, $value);
765 } //end if
766 } //end if
767 else
768 if (!empty ($value)) {
769 foreach ($value as $k => $v) {
770 if ($v != '') {
771 if (!is_array($v)) {
772 $data['jellyBeans'][] = $this->makeJellyBean($url, $key, $k, $v);
773 } else {
774 foreach ($v as $kk => $vv) {
775 $data['jellyBeans'][] = $this->makeJellyBean($url, $key, $kk, $vv);
776 } // end inner inner foreach
777 }
778 }
779 } // end inner foreach
780 }
781 } //end foreach
782 } //end if
783
784 return $data;
785 }
786
787 /**
788 * preps an array to be a jellybean
789 * */
790 private function makeJellyBean($url, $key, $k, $v) {
791 $dkey = null;
792 if ($key == 'p_value') {
793 //HACK
794
795 $nkey = $text = (stristr($k, 'http://')) ? $v : $k;
796 $nvalue = ($nkey == $k) ? $v : $k;
797 //(stristr($k,'http://')) ?$v:$k;
798
799 if (!empty ($text)) {
800 $dkey = SONIA_H :: encodeURI($nkey);
801 $dvaluex = SONIA_H :: encodeURI($nvalue);
802 $dremove = $url . '&remove=' . $dkey . '&vv=' . $dvaluex;
803 $dvalue = rawurldecode($text);
804 $dfacet = true;
805 }
806 } else
807 if ($key == 'facet') {
808 if ($v != '' && !is_array($v)) {
809 $dkey = SONIA_H :: encodeURI($k);
810 $vval = SONIA_H :: encodeURI($v);
811 $dremove = $url . '&remove=' . $key . '&vv=' . $vval;
812 $dvalue = $this->sonia->getLabel($v);
813 $dfacet = true;
814 }
815 } else
816 if ($key == 'cat') {
817 if ($v != '' && !is_array($v)) {
818 $dkey = SONIA_H :: encodeURI($k);
819 $vval = SONIA_H :: encodeURI($v);
820 $dremove = $url . '&remove=' . $dkey . '&vv=' . $vval;
821 $dvalue = $this->sonia->getLabel($v);
822 $dfacet = true;
823 }
824 } else
825 if ($key == 'qstring') {
826 if ($v != '' && !is_array($v)) {
827 $dkey = SONIA_H :: encodeURI($k);
828 $vval = SONIA_H :: encodeURI($v);
829 $dremove = $url . '&remove=qstring&vn=' . $dkey . '&vv=' . $vval;
830 $dvalue = $v;
831 $dfacet = false;
832 }
833 } else {
834 $dkey = SONIA_H :: encodeURI($k);
835 $dremove = $url . '&remove=' . $dkey;
836 if ($this->sonia->classExists($k))
837 $dvalue = rawurldecode($this->sonia->getLabel($k));
838 if ($this->sonia->propertyExists($k))
839 $dvalue = rawurldecode($this->sonia->getPropertyName($k));
840 $dfacet = true;
841 }
842
843 //if there is only one term left, removing it should restart the search
844 if ($this->getSearchTermCount() == 1) $dremove = $this->getRestartSearchLink();
845
846 if ($dkey != null) {
847 _semantic_search_str_clean($dvalue);
848 return array (
849 $dkey,
850 $dvalue,
851 $dremove,
852 $dfacet
853 );
854 }
855 }
856
857 /**
858 * provide the number of search terms
859 * */
860 private function getSearchTermCount(){
861 return count($this->sonia->sonia_search_array);
862 }
863
864
865 public function getRestartSearchLink() {
866 return NinaNav :: search_reset_link($this->sonia);
867 }
868
869 /**
870 * */
871 function jellyBeanMenu($url) {
872 $data = array ();
873 $data['url'] = $url;
874 $data['restart'] = $this->getRestartSearchLink();
875
876 $the_type = 'semantic_search_help_text';
877 $data['help_text'] = variable_get($the_type, t('set in admin/settings/semantic_search'));
878
879 $the_type = 'semantic_search_help_title';
880 $data['help_title'] = variable_get($the_type, t('set in admin/settings/semantic_search'));
881
882 return $data;
883 }
884
885 /**
886 * parse search results into data for display
887 *
888 * */
889 function searchResults($not_rpc = true) {
890 //__nsp(' semantic_search.search.inc searchResults() start');
891 $rdata = array ();
892 if (empty ($this->search_results))
893 $this->search_results = array ();
894
895 $count = 0;
896 $total = 0;
897 $invisible_key = 5;
898 $no_count = 0;
899 $yes_count = 0;
900
901 //how many to display per page
902 $rdata['displayed'] = $displayed = SEMANTIC_SEARCH_DISPLAY_PAGINATION;
903 $rdata['base_url'] = $base_url = $this->makefullURLforRedirect();
904 $rdata['begin'] = $begin = $this->sonia->io->getPaginationBegin();
905
906 $rdata['not_rpc'] = $not_rpc;
907 $rdata['total'] = count($this->search_results);
908 $rdata['do_search'] = $this->doSearch();
909
910 if (!$rdata['do_search'] && empty ($this->search_results)) {
911 //__nsp(' semantic_search.search.inc searchResults() empty end');
912 print_r($this->search_results,'$this->search_results ev');
913 return array ();
914 }
915 //search_results DATA STRUCTURE
916 //TODO move to constants
917 //search_results[0] ['#node_url'],['#result_number'],['#title'],
918 //..[rows]// 0 name 1 value 2 searched 3 uri/key 4 hidden
919 //.. 5 type (document, image, url, email) 6 count(order number)
920
921 $rdata['search_results'] = array ();
922
923 $_SESSION['sonia_latest_search'] = $base_url . '&' . SEMANTIC_SEARCH_PAGINATION_SIGNAL . '=' . $begin;
924
925 $the_type = 'semantic_search_display_preview_data';
926 $display_all_paramter_counts = variable_get($the_type, '0');
927 $cached_prefs = array ();
928 print_r($this->search_results,'$this->search_results');
929 foreach ($this->search_results as $key => $value) {
930 $search_result = array ();
931
932 $hidden_search_results = $main_search_results = array ();
933 $inner_count = 0;
934
935 if (true) {
936 $nid = SONIA_H :: getIDFromURI($key);
937 $url = url('node/' . $nid, null, null, true);
938
939 $search_result['#node_url'] = $url;
940 $search_result['#result_number'] = $count +1;
941
942 $thtml = '';
943 $hhtml = '';
944
945 $param_count = 1;
946 $param_display_data = array ();
947
948 foreach ($value as $data) {
949 $property = $data['p'];
950 $object = $data['o'];
951 $property_label = (isset ($data['p_l'])) ? $data['p_l'] : '';
952
953 //the array of params for this result row
954 $display = array ();
955
956 //set up display order
957 $fid = SONIA_H :: getIDFromURI($property);
958 $pref_name2 = 'display_result_' . $fid;
959 if (isset ($cached_prefs[$pref_name2])) {
960 $display_visibly = $cached_prefs[$pref_name2];
961 } else {
962 $display_visibly = variable_get($pref_name2, '4');
963 $cached_prefs[$pref_name2] = $display_visibly;
964 }
965
966 $is_file_type = !empty ($data['fname']) || strstr($object,SONIA_FILE_CLASS_URI);
967 $display_order = 10 + $display_visibly;
968
969 //create $display
970 //place $display in sorting array
971 $display['name'] = _semantic_search_str_clean($property_label);
972 $display['uri'] = SONIA_H :: encodeURI($property);
973 if ($property_label != null) {
974
975 $param_in_search = $display['searched'] = $this->sonia->checkPropertyInSearch($property, $object);
976 } else
977 $display['searched'] = false;
978
979 $display['hidden'] = ($display_visibly == $invisible_key && !$display['searched']);
980 $display['title'] = false;
981 $display['image'] = false;
982
983 if ($property == SONIA_RDFS_CREATE_LABEL) {
984 //always first
985 $search_result['#title'] = _semantic_search_str_clean($object);
986 $display['title'] = true;
987
988 } else
989 if ($is_file_type) {
990 $file_pref_name = 'display_image_' . $fid;
991 $thidden = !variable_get($file_pref_name, '0');
992 if (!$thidden) {
993 $display['hidden'] = $thidden;
994 $path = $data['fpath'];
995 $path = str_replace($data['fname'], rawurlencode($data['fname']), $path);
996
997 if ($path != '') {
998 $image_src_url = '/' . $path;
999 //add description/title ?
1000 $display['image'] = true;
1001 $display['value'] = $image_src_url;
1002 $param_display_data[$display_order][$param_count] = $display;
1003
1004 } else {
1005 //this image is a phony should be deleted from the db
1006 $display = array();
1007 }
1008 } else {
1009 $display = array();
1010 }
1011 } else
1012 if ($property == SONIA_RDF_CREATE_TYPE) {
1013 //check to see if this class type is permitted to be displayed in search results
1014 $content_type = SONIA_H :: getIDFromURI($object);
1015 $pref_name = 'display_' . $content_type;
1016 //$allowed = variable_get($pref_name, '1');
1017 //debug
1018 //if (!$allowed) dbg($pref_name,'$pref_name not allowed');
1019
1020 } else
1021 if ($property_label != null) {
1022 $display['value'] = _semantic_search_str_clean($object);
1023 if ($display['title']) {
1024 //makes the search result show up first
1025 $param_display_data[0][0] = $display;
1026 } else
1027 if ($param_in_search) {
1028
1029 // used by themers to highlight text optionally
1030 if (isset ($this->search_params['qstring'])) {
1031 foreach ($this->search_params['qstring'] as $term) {
1032 if (!is_array($term)) {
1033 $display['qstring'][] = $term;
1034 } else {
1035 foreach ($term as $tterm) {
1036 $display['qstring'][] = $tterm;
1037 }
1038 }
1039 }
1040 }
1041
1042 $param_display_data[0][$param_count] = $display;
1043 } else {
1044 $param_display_data[$display_order][$param_count] = $display;
1045 }
1046 } //end property label has value
1047 $param_count++;
1048 } // end param foreach
1049
1050 //complete and sort
1051 if (true) {
1052 // add to data array
1053 $search_result['rows'] = array ();
1054
1055 ksort($param_display_data);
1056 foreach ($param_display_data as $pdata) {
1057 ksort($pdata);
1058 foreach ($pdata as $display) {
1059 $search_result['rows'][] = $display;
1060 }
1061 }
1062
1063 $rdata['search_results'][] = $search_result;
1064
1065 } else {
1066 $this->notAllowedResultMessage($count);
1067 }
1068
1069 } else {
1070 //later this can be to retrieve items other than objects
1071 $this->badResultMessage();
1072 }
1073 $count++;
1074 } //end search results foreach
1075 //__nsp(' semantic_search.search.inc searchResults() end');
1076
1077 return $rdata;
1078 }
1079
1080 /***/
1081 private function