Documentation TYPO3 par Ameos |
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) 2001-2004 Kasper Skaarhoj (kasperYYYY@typo3.com) 00006 * All rights reserved 00007 * 00008 * This script is part of the TYPO3 project. The TYPO3 project is 00009 * free software; you can redistribute it and/or modify 00010 * it under the terms of the GNU General Public License as published by 00011 * the Free Software Foundation; either version 2 of the License, or 00012 * (at your option) any later version. 00013 * 00014 * The GNU General Public License can be found at 00015 * http://www.gnu.org/copyleft/gpl.html. 00016 * A copy is found in the textfile GPL.txt and important notices to the license 00017 * from the author is found in LICENSE.txt distributed with these scripts. 00018 * 00019 * 00020 * This script is distributed in the hope that it will be useful, 00021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00023 * GNU General Public License for more details. 00024 * 00025 * This copyright notice MUST APPEAR in all copies of the script! 00026 ***************************************************************/ 00042 require_once(PATH_tslib."class.tslib_pibase.php"); 00043 require_once(PATH_tslib."class.tslib_search.php"); 00044 require_once(t3lib_extMgm::extPath("indexed_search")."class.indexer.php"); 00045 00046 class tx_indexedsearch extends tslib_pibase { 00047 var $prefixId = "tx_indexedsearch"; // Same as class name 00048 var $scriptRelPath = "pi/class.tx_indexedsearch.php"; // Path to this script relative to the extension dir. 00049 var $extKey = "indexed_search"; // The extension key. 00050 var $join_pages=0; // See document for info about this flag... 00051 00052 var $defaultResultNumber=20; 00053 var $wholeSiteIdList = 0; 00054 00055 var $operator_translate_table = Array ( // case-sensitiv. Defineres the words, which will be operators between words 00056 Array ("+" , "AND"), 00057 Array ("|" , "OR"), 00058 Array ("-" , "AND NOT"), 00059 // english 00060 # Array ("AND" , "AND"), 00061 # Array ("OR" , "OR"), 00062 # Array ("NOT" , "AND NOT"), 00063 ); 00064 00065 // Internals: 00066 var $cache_path=array(); 00067 var $cache_rl=array(); 00068 var $fe_groups_required=array(); 00069 var $domain_records=array(); 00070 var $sWArr=array(); 00071 var $wSelClauses=array(); 00072 var $firstRow=array(); 00073 var $resultSections=array(); 00074 00075 var $anchorPrefix = ''; // Prefix for local anchors. For "speaking URLs" to work with <base>-url set. 00076 00077 function main($content,$conf) { 00078 $this->conf=$conf; 00079 $this->pi_loadLL(); 00080 $this->pi_setPiVarDefaults(); 00081 $this->anchorPrefix = substr(t3lib_div::getIndpEnv('TYPO3_REQUEST_URL'),strlen(t3lib_div::getIndpEnv('TYPO3_SITE_URL'))); 00082 00083 #debug($this->piVars); 00084 00085 // Initialize the indexer-class - just to use a few function (for making hashes) 00086 $this->indexerObj = t3lib_div::makeInstance("tx_indexedsearch_indexer"); 00087 00088 // If "_sections" is set, this value overrides any existing value. 00089 if ($this->piVars["_sections"]) $this->piVars["sections"] = $this->piVars["_sections"]; 00090 00091 // Add previous search words to current 00092 if ($this->piVars['sword_prev_include'] && $this->piVars["sword_prev"]) { 00093 $this->piVars["sword"] = trim($this->piVars["sword_prev"]).' '.$this->piVars["sword"]; 00094 } 00095 00096 // Selector-box values defined here: 00097 $optValues = Array( 00098 "type" => Array( 00099 "0" => $this->pi_getLL("opt_type_0"), 00100 "1" => $this->pi_getLL("opt_type_1"), 00101 "2" => $this->pi_getLL("opt_type_2"), 00102 "3" => $this->pi_getLL("opt_type_3"), 00103 "10" => $this->pi_getLL("opt_type_10"), 00104 "20" => $this->pi_getLL("opt_type_20"), 00105 ), 00106 "defOp" => Array( 00107 "0" => $this->pi_getLL("opt_defOp_0"), 00108 "1" => $this->pi_getLL("opt_defOp_1"), 00109 ), 00110 "sections" => Array( 00111 "0" => $this->pi_getLL("opt_sections_0"), 00112 "-1" => $this->pi_getLL("opt_sections_-1"), 00113 "-2" => $this->pi_getLL("opt_sections_-2"), 00114 "-3" => $this->pi_getLL("opt_sections_-3"), 00115 // Here values like "rl1_" and "rl2_" + a rootlevel 1/2 id can be added to perform searches in rootlevel 1+2 specifically. The id-values can even be commaseparated. Eg. "rl1_1,2" would search for stuff inside pages on menu-level 1 which has the uid's 1 and 2. 00116 ), 00117 "media" => Array( 00118 "-1" => $this->pi_getLL("opt_media_-1"), 00119 "0" => $this->pi_getLL("opt_media_0"), 00120 "-2" => $this->pi_getLL("opt_media_-2"), 00121 "1" => $this->pi_getLL("opt_media_1"), 00122 "2" => $this->pi_getLL("opt_media_2"), 00123 "3" => $this->pi_getLL("opt_media_3"), 00124 ), 00125 "order" => Array( 00126 "rank_flag" => $this->pi_getLL("opt_order_rank_flag"), 00127 "rank_freq" => $this->pi_getLL("opt_order_rank_freq"), 00128 "rank_first" => $this->pi_getLL("opt_order_rank_first"), 00129 "rank_count" => $this->pi_getLL("opt_order_rank_count"), 00130 "mtime" => $this->pi_getLL("opt_order_mtime"), 00131 "title" => $this->pi_getLL("opt_order_title"), 00132 "crdate" => $this->pi_getLL("opt_order_crdate"), 00133 # "rating" => "Page-rating", 00134 # "hits" => "Page-hits", 00135 ), 00136 "group" => Array ( 00137 "sections" => $this->pi_getLL("opt_group_sections"), 00138 "flat" => $this->pi_getLL("opt_group_flat"), 00139 ), 00140 "lang" => Array ( 00141 -1 => $this->pi_getLL("opt_lang_-1"), 00142 0 => $this->pi_getLL("opt_lang_0"), 00143 ), 00144 "desc" => Array ( 00145 "0" => $this->pi_getLL("opt_desc_0"), 00146 "1" => $this->pi_getLL("opt_desc_1"), 00147 ), 00148 "results" => Array ( 00149 "10" => "10", 00150 "20" => "20", 00151 "50" => "50", 00152 "100" => "100", 00153 ) 00154 ); 00155 00156 $this->operator_translate_table[]=Array ($this->pi_getLL("local_operator_AND") , "AND"); 00157 $this->operator_translate_table[]=Array ($this->pi_getLL("local_operator_OR") , "OR"); 00158 $this->operator_translate_table[]=Array ($this->pi_getLL("local_operator_NOT") , "AND NOT"); 00159 00160 // This is the id of the site root. This value may be a commalist of integer (prepared for this) 00161 $this->wholeSiteIdList=intval($GLOBALS["TSFE"]->config["rootLine"][0]["uid"]); 00162 00163 // This selects the first and secondary menus for the "sections" selector - so we can search in sections and sub sections. 00164 if ($this->conf["show."]["L1sections"]) { 00165 $firstLevelMenu = $this->getMenu($this->wholeSiteIdList); 00166 # debug($firstLevelMenu); 00167 while(list($kk,$mR)=each($firstLevelMenu)) { 00168 if ($mR["doktype"]!=5) { 00169 $optValues["sections"]["rl1_".$mR["uid"]]=trim($this->pi_getLL("opt_RL1")." ".$mR["title"]); 00170 if ($this->conf["show."]["L2sections"]) { 00171 $secondLevelMenu = $this->getMenu($mR["uid"]); 00172 while(list($kk2,$mR2)=each($secondLevelMenu)) { 00173 if ($mR["doktype"]!=5) { 00174 $optValues["sections"]["rl2_".$mR2["uid"]]=trim($this->pi_getLL("opt_RL2")." ".$mR2["title"]); 00175 } else unset($secondLevelMenu[$kk2]); 00176 } 00177 $optValues["sections"]["rl2_".implode(",",array_keys($secondLevelMenu))]=$this->pi_getLL("opt_RL2ALL"); 00178 } 00179 } else unset($firstLevelMenu[$kk]); 00180 } 00181 $optValues["sections"]["rl1_".implode(",",array_keys($firstLevelMenu))]=$this->pi_getLL("opt_RL1ALL"); 00182 } 00183 00184 // This happens AFTER the use of $this->wholeSiteIdList above because the above will then fetch the menu for the CURRENT site - regardless of this kind of searching here. Thus a general search will lookup in the WHOLE database while a specific section search will take the current sections... 00185 if ($this->conf["search."]["rootPidList"]) { 00186 $this->wholeSiteIdList = implode(",",t3lib_div::intExplode(",",$this->conf["search."]["rootPidList"])); 00187 #debug($this->wholeSiteIdList); 00188 } 00189 00190 00191 // Add search languages: 00192 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', 'sys_language', '1'.$this->cObj->enableFields('sys_language')); 00193 while($lR = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { 00194 $optValues["lang"][$lR["uid"]]=$lR["title"]; 00195 } 00196 00197 00198 00199 #debug($this->piVars); 00200 // Setting first values in optValues as default values IF there is not corresponding piVar value set already. 00201 reset($optValues); 00202 while(list($kk,$vv)=each($optValues)) { 00203 if (!isset($this->piVars[$kk])) { 00204 reset($vv); 00205 $this->piVars[$kk]=key($vv); 00206 } 00207 } 00208 #debug($this->piVars); 00209 00210 // Blind selectors: 00211 if (is_array($this->conf["blind."])) { 00212 reset($this->conf["blind."]); 00213 while(list($kk,$vv)=each($this->conf["blind."])) { 00214 if (is_array($vv)) { 00215 reset($vv); 00216 while(list($kkk,$vvv)=each($vv)) { 00217 if (!is_array($vvv) && $vvv && is_array($optValues[substr($kk,0,-1)])) { 00218 unset($optValues[substr($kk,0,-1)][$kkk]); 00219 } 00220 } 00221 } elseif ($vv) { // If value is not set, unset the option array. 00222 unset($optValues[$kk]); 00223 } 00224 } 00225 } 00226 00227 00228 // This gets the search-words into the $sWArr: 00229 $this->sWArr = $sWArr = $this->getSearchWords($this->piVars["defOp"]); 00230 #debug($this->sWArr); 00231 00232 // If there was any search words entered... 00233 if (is_array($sWArr)) { 00234 $content = $this->doSearch($sWArr); 00235 } // END: There was a search word. 00236 00237 // Finally compile all the content, form, messages and results: 00238 $content= 00239 $this->makeSearchForm($optValues). 00240 $this->printRules(). 00241 $content; 00242 00243 return $this->pi_wrapInBaseClass($content); 00244 } 00245 00249 function doSearch($sWArr) { 00250 $rowcontent=""; 00251 $pt1=t3lib_div::milliseconds(); 00252 00253 // This SEARCHES for the searchwords in $sWArr AND returns a COMPLETE list of phash-integers of the matches. 00254 $list = $this->getPhashList($sWArr); 00255 $pt2=t3lib_div::milliseconds(); 00256 00257 00258 // IF there were any matches (there is results...) then go on. 00259 if ($list) { 00260 $GLOBALS["TT"]->push("Searching Final result"); 00261 00262 // Do the search: 00263 $res = $this->execFinalQuery($list); 00264 00265 // Get some variables: 00266 $count = $GLOBALS['TYPO3_DB']->sql_num_rows($res); 00267 #debug($count); 00268 $this->piVars["results"] = $displayCount = t3lib_div::intInRange($this->piVars["results"],1,100000,$this->defaultResultNumber); 00269 $pointer=t3lib_div::intInRange($this->piVars["pointer"],0,floor($count/$displayCount)); 00270 00271 $pt3=t3lib_div::milliseconds(); 00272 00273 // Now, traverse result and put the rows to be displayed into an array 00274 $lines=Array(); 00275 $c=0; 00276 $this->firstRow=Array(); // Will hold the first row in result - used to calculate relative hit-ratings. 00277 $this->resultRows=Array(); // Will hold the results rows for display. 00278 $this->grouping_phashes=array(); // Used to filter out duplicates. 00279 $this->grouping_chashes=array(); // Used to filter out duplicates BASED ON cHash. 00280 while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { 00281 if (!$c) { 00282 $this->firstRow=$row; 00283 } 00284 00285 $row["show_resume"]=$this->checkResume($row); 00286 $phashGr = !in_array($row["phash_grouping"],$this->grouping_phashes); 00287 $chashGr = !in_array($row["contentHash"].".".$row["data_page_id"],$this->grouping_chashes); 00288 if ($phashGr && $chashGr) { 00289 if ($row["show_resume"]) { // Only if the resume may be shown are we going to filter out duplicates... 00290 if ($row["item_type"]!=2) { // Only on documents which are not PDF files. 00291 $this->grouping_phashes[]=$row["phash_grouping"]; 00292 } 00293 $this->grouping_chashes[]=$row["contentHash"].".".$row["data_page_id"]; 00294 } 00295 $c++; 00296 00297 // All rows for display is put into resultRows[] 00298 if ($c > $pointer*$displayCount) { 00299 $row["result_number"]=$c; 00300 $this->resultRows[] = $row; 00301 if ($c+1 > ($pointer+1)*$displayCount) break; 00302 } 00303 } else { 00304 $count--; // For each time a phash_grouping document is found (which is thus not displayed) the search-result count is reduced, so that it matches the number of rows displayed. 00305 # debug(); 00306 } 00307 } 00308 $GLOBALS["TT"]->pull(); 00309 00310 #debug($this->resultRows); 00311 #debug(count($this->resultRows)); 00312 #debug($this->grouping_chashes); 00313 00314 $GLOBALS["TT"]->push("Display Final result"); 00315 00316 // SO, on to the result display here: 00317 $rowcontent.=$this->compileResult($this->resultRows); 00318 $pt4=t3lib_div::milliseconds(); 00319 00320 // Makes the result browsing navigation (next/prev, 1-2-3) 00321 # $PS = $this->makePointerSelector($count,$displayCount,$pointer); 00322 00323 00324 // Browsing box: 00325 if ($count) { 00326 #$content.=$PS."<HR>"; 00327 $this->internal["res_count"]=$count; 00328 $this->internal["results_at_a_time"]=$displayCount; 00329 $this->internal["maxPages"]=t3lib_div::intInRange($this->conf["search."]["page_links"],1,100,10); 00330 $addString = ($count&&$this->piVars["group"]=="sections"?" ".sprintf($this->pi_getLL("inNsection".(count($this->resultSections)>1?"s":"")),count($this->resultSections)):""); 00331 $browseBox1 = $this->pi_list_browseresults(1,$addString,$this->printResultSectionLinks()); 00332 $browseBox2 = $this->pi_list_browseresults(0); 00333 } 00334 00335 // Print the time the search took: 00336 if ($pt1 && $this->conf["show."]["parsetimes"]) { 00337 $parsetimes=""; 00338 $parsetimes.="<p>Word Search took: ".($pt2-$pt1)." ms<BR>"; 00339 $parsetimes.="Order Search took: ".($pt3-$pt2)." ms<BR>"; 00340 $parsetimes.="Display took: ".($pt4-$pt3)." ms</p><HR>"; 00341 } 00342 00343 // Browsing nav, bottom. 00344 if ($count) { 00345 $content=$browseBox1.$rowcontent.$browseBox2; 00346 } else { 00347 $content='<p'.$this->pi_classParam("noresults").'>'.$this->pi_getLL("noResults").'</p>'; 00348 } 00349 $content.=$parsetimes; 00350 00351 $GLOBALS["TT"]->pull(); 00352 } else { // No results found: 00353 $content.='<p'.$this->pi_classParam("noresults").'>'.$this->pi_getLL("noResults").'</p>'; 00354 } 00355 00356 // Print a message telling which words we searched for, and in which sections etc. 00357 $what=$this->tellUsWhatIsSeachedFor($sWArr). 00358 (substr($this->piVars["sections"],0,2)=="rl"?" ".$this->pi_getLL("inSection")." '".substr($this->getPathFromPageId(substr($this->piVars["sections"],4)),1)."'":""); 00359 $what='<div'.$this->pi_classParam("whatis").'><p>'.$what.'</p></div>'; 00360 $content=$what.$content; 00361 00362 // Write search statistics 00363 $this->writeSearchStat($sWArr,$count,array($pt1,$pt2,$pt3,$pt4)); 00364 return $content; 00365 } 00366 00367 00368 00369 00370 00371 00372 /*********************************** 00373 00374 SEARCHING FUNCTIONS 00375 00376 ***********************************/ 00377 00378 00379 00380 00381 00382 00383 00396 function getSearchWords($defOp) { 00397 $inSW = substr($this->piVars["sword"],0,200); 00398 if ($this->piVars["type"]==20) { 00399 return array(array("sword"=>trim($inSW),"oper"=>"AND")); 00400 } else { 00401 $search = t3lib_div::makeInstance("tslib_search"); 00402 $search->default_operator = $defOp==1 ? 'OR' : 'AND'; 00403 $search->operator_translate_table = $this->operator_translate_table; 00404 $search->register_and_explode_search_string($inSW); 00405 00406 if (is_array($search->sword_array)) { 00407 return $search->sword_array; 00408 } 00409 } 00410 } 00411 00416 function getPhashList($sWArr) { 00417 $c=0; 00418 00419 $totalHashList=array(); // This array accumulates the phash-values 00420 $this->wSelClauses=array(); 00421 00422 reset($sWArr); 00423 while(list($k,$v)=each($sWArr)) { 00424 $sWord = $this->indexerObj->strtolower_all($v["sword"]); // lower-case all of them... 00425 00426 $GLOBALS["TT"]->push("SearchWord ".$sWord); 00427 00428 $plusQ=""; 00429 /* 00430 // Maybe this will improve the search queries. Tests has shown it not to do so though... 00431 if (count($totalHashList)) { 00432 switch($v["oper"]) { 00433 case "OR": 00434 $plusQ = "AND IR.phash NOT IN (".implode(",",$totalHashList).")"; 00435 break; 00436 case "AND NOT": 00437 default: // AND 00438 $plusQ = "AND IR.phash IN (".implode(",",$totalHashList).")"; 00439 break; 00440 } 00441 } 00442 $plusQ=""; 00443 */ 00444 00445 // Making the query for a single search word based on the search-type 00446 $res=""; 00447 $theType = (string)$this->piVars["type"]; 00448 if (strstr($sWord," ")) $theType=20; // If there are spaces in the search-word, make a full text search instead. 00449 00450 $wSel=""; 00451 switch($theType) { 00452 case "1": 00453 $wSel = "IW.baseword LIKE '%".$GLOBALS['TYPO3_DB']->quoteStr($sWord, 'index_words')."%'"; 00454 $res = $this->execPHashListQuery($wSel,$plusQ); 00455 00456 break; 00457 case "2": 00458 $wSel = "IW.baseword LIKE '".$GLOBALS['TYPO3_DB']->quoteStr($sWord, 'index_words')."%'"; 00459 $res = $this->execPHashListQuery($wSel,$plusQ); 00460 break; 00461 case "3": 00462 $wSel = "IW.baseword LIKE '%".$GLOBALS['TYPO3_DB']->quoteStr($sWord, 'index_words')."'"; 00463 $res = $this->execPHashListQuery($wSel,$plusQ); 00464 break; 00465 case "10": 00466 $wSel = "IW.metaphone = ".$this->indexerObj->metaphone($sWord); 00467 $res = $this->execPHashListQuery($wSel,$plusQ); 00468 break; 00469 case "20": 00470 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery( 00471 'ISEC.phash', 00472 'index_section AS ISEC, index_fulltext AS IFT', 00473 'IFT.fulltextdata LIKE "%'.$GLOBALS['TYPO3_DB']->quoteStr($sWord, 'index_fulltext').'%" AND 00474 ISEC.phash = IFT.phash 00475 '.$this->sectionTableWhere(), 00476 'ISEC.phash' 00477 ); 00478 $wSel = "1=1"; 00479 00480 if ($this->piVars["type"]==20) $this->piVars["order"]="mtime"; // If there is a fulltext search for a sentence there is a likelyness that sorting cannot be done by the rankings from the rel-table (because no relations will exist for the sentence in the word-table). So therefore mtime is used instaed. It is not required, but otherwise some hits may be left out. 00481 break; 00482 default: 00483 $wSel = "IW.wid = ".$hash = $this->indexerObj->md5inthash($sWord); 00484 $res = $this->execPHashListQuery($wSel,$plusQ); 00485 break; 00486 } 00487 $this->wSelClauses[]=$wSel; 00488 00489 // If there was a query to do, then select all phash-integers which resulted from this. 00490 if ($res) { 00491 00492 # Get phash list by searching for it: 00493 $phashList = array(); 00494 while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { 00495 $phashList[]=$row["phash"]; 00496 } 00497 $GLOBALS['TYPO3_DB']->sql_free_result($res); 00498 00499 // Here the phash list are merged with the existing result based on whether we are dealing with OR, NOT or AND operations. 00500 if ($c) { 00501 switch($v["oper"]) { 00502 case "OR": 00503 $totalHashList=array_unique(array_merge($phashList,$totalHashList)); 00504 break; 00505 case "AND NOT": 00506 $totalHashList=array_diff($totalHashList,$phashList); 00507 break; 00508 default: // AND... 00509 $totalHashList=array_intersect($totalHashList,$phashList); 00510 break; 00511 } 00512 } else { 00513 $totalHashList=$phashList; // First search 00514 } 00515 #debug($totalHashList); 00516 } 00517 00518 $GLOBALS["TT"]->pull(); 00519 $c++; 00520 } 00521 00522 #debug($sWArr); 00523 return implode(",",$totalHashList); 00524 } 00525 00529 function execPHashListQuery($wordSel,$plusQ="") { 00530 return $GLOBALS['TYPO3_DB']->exec_SELECTquery( 00531 'STRAIGHT_JOIN IR.phash', 00532 'index_words AS IW, 00533 index_rel AS IR, 00534 index_section AS ISEC', 00535 $wordSel.' 00536 AND IW.wid=IR.wid 00537 AND ISEC.phash = IR.phash 00538 '.$this->sectionTableWhere().' 00539 '.$plusQ, 00540 'IR.phash' 00541 ); 00542 } 00543 00547 function sectionTableWhere() { 00548 #debug($this->piVars["sections"]); 00549 $out = $this->wholeSiteIdList<0 ? "" : "AND ISEC.rl0 IN (".$this->wholeSiteIdList.")"; 00550 $list = implode(",",t3lib_div::intExplode(",",substr($this->piVars["sections"],4))); 00551 00552 if (substr($this->piVars["sections"],0,4)=="rl1_") { 00553 $out.= "AND ISEC.rl1 IN (".$list.")"; 00554 } else if (substr($this->piVars["sections"],0,4)=="rl2_") { 00555 $out.= "AND ISEC.rl2 IN (".$list.")"; 00556 } else { 00557 switch((string)$this->piVars["sections"]) { 00558 case "-1": // "-1" => "Only this page", 00559 $out.= " AND ISEC.page_id=".$GLOBALS["TSFE"]->id; 00560 break; 00561 case "-2": // "-2" => "Top + level 1", 00562 $out.= " AND ISEC.rl2=0"; 00563 break; 00564 case "-3": // "-3" => "Level 2 and out", 00565 $out.= " AND ISEC.rl2>0"; 00566 break; 00567 } 00568 } 00569 return $out; 00570 } 00571 00575 function mediaTypeWhere() { 00576 switch($this->piVars["media"]) { 00577 case 0: // "0" => "Kun TYPO3 sider", 00578 $out = "AND IP.item_type=0"; 00579 break; 00580 case 1: // "1" => "Kun HTML dokumenter", 00581 $out = "AND IP.item_type=1"; 00582 break; 00583 case 2: // "2" => "Kun PDF dokumenter", 00584 $out = "AND IP.item_type=2"; 00585 break; 00586 case 3: // "3" => "Kun Word dokumenter", 00587 $out = "AND IP.item_type=3"; 00588 break; 00589 case -2: // All external documents 00590 $out = "AND IP.item_type>0"; 00591 break; 00592 case -1: 00593 default: 00594 $out=""; 00595 break; 00596 } 00597 return $out; 00598 } 00599 00603 function languageWhere() { 00604 if ($this->piVars["lang"]>=0) { // -1 is the same as ALL language. 00605 return "AND IP.sys_language_uid=".intval($this->piVars["lang"]); 00606 } 00607 } 00608 00612 function execFinalQuery($list) { 00613 00614 $page_join=""; 00615 $page_where=""; 00616 if ($this->join_pages) { 00617 $page_join = ", 00618 pages"; 00619 $page_where = "pages.uid = ISEC.page_id 00620 ".$this->cObj->enableFields("pages")." 00621 AND pages.no_search=0 00622 AND pages.doktype<200 00623 "; 00624 } elseif ($this->wholeSiteIdList>=0) { 00625 $siteIdNumbers = t3lib_div::intExplode(",",$this->wholeSiteIdList); 00626 $id_list=array(); 00627 while(list(,$rootId)=each($siteIdNumbers)) { 00628 $id_list[]=$this->cObj->getTreeList($rootId,9999,0,0,"","").$rootId; 00629 } 00630 $page_where = "ISEC.page_id IN (".implode(",",$id_list).")"; 00631 } else { 00632 $page_where = " 1=1 "; 00633 } 00634 00635 // If any of the ranking sortings are selected, we must make a join with the word/rel-table again, because we need to calculate ranking based on all search-words found. 00636 if (substr($this->piVars["order"],0,5)=="rank_") { 00637 /* 00638 OK there were some fancy calculations promoted by Graeme Merrall: 00639 00640 "However, regarding relevance you probably want to look at something like 00641 Salton's formula which is a good easy way to measure relevance. 00642 Oracle Intermedia uses this and it's pretty simple: 00643 Score can be between 0 and 100, but the top-scoring document in the query 00644 will not necessarily have a score of 100 -- scoring is relative, not 00645 absolute. This means that scores are not comparable across indexes, or even 00646 across different queries on the same index. Score for each document is 00647 computed using the standard Salton formula: 00648 00649 3f(1+log(N/n)) 00650 00651 Where f is the frequency of the search term in the document, N is the total 00652 number of rows in the table, and n is the number of rows which contain the 00653 search term. This is converted into an integer in the range 0 - 100. 00654 00655 There's a good doc on it at 00656 http://ls6-www.informatik.uni-dortmund.de/bib/fulltext/ir/Pfeifer:97/ 00657 although it may be a little complex for what you require so just pick the 00658 relevant parts out. 00659 " 00660 00661 However I chose not to go with this for several reasons. 00662 I do not claim that my ways of calculating importance here is the best. 00663 ANY (better) suggestions for ranking calculation is accepted! (as long as they are shipped with tested code in exchange for this.) 00664 */ 00665 00666 switch($this->piVars["order"]) { 00667 case "rank_flag": // This gives priority to word-position (max-value) so that words in title, keywords, description counts more than in content. 00668 // The ordering is refined with the frequency sum as well. 00669 $grsel = "MAX(IR.flags) AS order_val1, SUM(IR.freq) AS order_val2"; 00670 $orderBy = "order_val1".$this->isDescending().",order_val2".$this->isDescending(); 00671 break; 00672 case "rank_first": // Results in average position of search words on page. Must be inversely sorted (low numbers are closer to top) 00673 $grsel = "AVG(IR.first) AS order_val"; 00674 $orderBy = "order_val".$this->isDescending(1); 00675 break; 00676 case "rank_count": // Number of words found 00677 $grsel = "SUM(IR.count) AS order_val"; 00678 $orderBy = "order_val".$this->isDescending(); 00679 break; 00680 default: // Frequency sum. I'm not sure if this is the best way to do it (make a sum...). Or should it be the average? 00681 $grsel = "SUM(IR.freq) AS order_val"; 00682 $orderBy = "order_val".$this->isDescending(); 00683 break; 00684 } 00685 00686 // So, words are imploded into an OR statement (no "sentence search" should be done here - may deselect results) 00687 $wordSel='('.implode(' OR ',$this->wSelClauses).') AND '; 00688 00689 return $GLOBALS['TYPO3_DB']->exec_SELECTquery( 00690 'STRAIGHT_JOIN ISEC.*, IP.*, '.$grsel, 00691 'index_words AS IW, 00692 index_rel AS IR, 00693 index_section AS ISEC, 00694 index_phash AS IP'. 00695 $page_join, 00696 $wordSel.' 00697 IP.phash IN ('.$list.') '. 00698 $this->mediaTypeWhere().' '. 00699 $this->languageWhere().' 00700 AND IW.wid=IR.wid 00701 AND ISEC.phash = IR.phash 00702 AND IP.phash = IR.phash 00703 AND '.$page_where, 00704 'IP.phash', 00705 $orderBy 00706 ); 00707 } else { // Otherwise, if sorting are done with the pages table or other fields, there is no need for joining with the rel/word tables: 00708 00709 $orderBy = ''; 00710 switch((string)$this->piVars["order"]) { 00711 case "rating": 00712 debug("rating: NOT ACTIVE YET"); 00713 break; 00714 case "hits": 00715 debug("rating: NOT ACTIVE YET"); 00716 break; 00717 case "title": 00718 $orderBy = "IP.item_title".$this->isDescending(); 00719 break; 00720 case "crdate": 00721 $orderBy = "IP.item_crdate".$this->isDescending(); 00722 break; 00723 case "mtime": 00724 $orderBy = "IP.item_mtime".$this->isDescending(); 00725 break; 00726 } 00727 00728 return $GLOBALS['TYPO3_DB']->exec_SELECTquery( 00729 'ISEC.*, IP.*', 00730 'index_phash AS IP,index_section AS ISEC'.$page_join, 00731 'IP.phash IN ('.$list.') '. 00732 $this->mediaTypeWhere().' '. 00733 $this->languageWhere().' 00734 AND IP.phash = ISEC.phash 00735 AND '.$page_where, 00736 'IP.phash', 00737 $orderBy 00738 ); 00739 } 00740 } 00741 00745 function checkResume($row) { 00746 if ($row["item_type"]>0) { 00747 // phash_t3 is the phash of the parent TYPO3 page row which initiated the indexing of the documents in this section. 00748 00749 // So, selecting for the grlist records belonging to the parent phash-row where the current users gr_list exists. 00750 // If this is NOT found, there is still a theoretical possibility that another user accessible page would display a link, so maybe the resume of such a document here may be unjustified hidden. But this case is rare. 00751 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('phash', 'index_grlist', 'phash='.intval($row['phash_t3']).' AND gr_list="'.$GLOBALS['TYPO3_DB']->quoteStr($GLOBALS['TSFE']->gr_list, 'index_grlist').'"'); 00752 if ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) { 00753 # debug("Look up for external media '".$row["data_filename"]."': phash:".$row["phash_t3"]." YES - (".$GLOBALS["TSFE"]->gr_list.")!",1); 00754 return 1; 00755 } else { 00756 # debug("Look up for external media '".$row["data_filename"]."': phash:".$row["phash_t3"]." NO - (".$GLOBALS["TSFE"]->gr_list.")!",1); 00757 return 0; 00758 } 00759 } else { // ALm typo3 pages: 00760 if (strcmp($row["gr_list"],$GLOBALS["TSFE"]->gr_list)) { 00761 // Selecting for the grlist records belonging to the phash-row where the current users gr_list exists. If it is found it is proof that this user has direct access to the phash-rows content although he did not himself initiate the indexing... 00762 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('phash', 'index_grlist', 'phash='.intval($row['phash']).' AND gr_list="'.$GLOBALS['TYPO3_DB']->quoteStr($GLOBALS['TSFE']->gr_list, 'index_grlist').'"'); 00763 if ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) { 00764 #debug("Checking on it ...".$row["item_title"]."/".$row["phash"]." - YES (".$GLOBALS["TSFE"]->gr_list.")",1); 00765 return 1; 00766 } else { 00767 #debug("Checking on it ...".$row["item_title"]."/".$row["phash"]." - NOPE",1); 00768 return 0; 00769 } 00770 } else { 00771 #debug("Resume can be shown, because the document was in fact indexed by this combination of groups!".$GLOBALS["TSFE"]->gr_list." - ".$row["item_title"]."/".$row["phash"],1); 00772 return 1; 00773 } 00774 } 00775 } 00776 00780 function isDescending($inverse=0) { 00781 $desc = $this->piVars["desc"]; 00782 if ($inverse) $desc=!$desc; 00783 return !$desc ? " DESC":""; 00784 } 00785 00789 function writeSearchStat($sWArr,$count,$pt) { 00790 $insertFields = array( 00791 'searchstring' => $this->piVars['sword'], 00792 'searchoptions' => serialize(array($this->piVars,$sWArr,$pt)), 00793 'feuser_id' => $this->fe_user->user['uid'], // fe_user id, integer 00794 'cookie' => $this->fe_user->id, // cookie as set or retrieve. If people has cookies disabled this will vary all the time... 00795 'IP' => t3lib_div::getIndpEnv('REMOTE_ADDR'), // Remote IP address 00796 'hits' => intval($count), // Number of hits on the search. 00797 'tstamp' => $GLOBALS['EXEC_TIME'] // Time stamp 00798 ); 00799 00800 $GLOBALS['TYPO3_DB']->exec_INSERTquery('index_stat_search', $insertFields); 00801 $newId = $GLOBALS['TYPO3_DB']->sql_insert_id(); 00802 00803 if ($newId) { 00804 foreach($sWArr as $val) { 00805 $insertFields = array( 00806 'word' => $this->indexerObj->strtolower_all($val['sword']), 00807 'index_stat_search_id' => $newId, 00808 'tstamp' => $GLOBALS['EXEC_TIME'] // Time stamp 00809 ); 00810 00811 $GLOBALS['TYPO3_DB']->exec_INSERTquery('index_stat_word', $insertFields); 00812 } 00813 } 00814 } 00815 00816 00817 00818 00819 00820 00821 00822 00823 00824 00825 00826 00827 00828 /*********************************** 00829 00830 LAYOUT FUNCTIONS 00831 00832 ***********************************/ 00833 00834 00835 00836 00840 function makeSearchForm($optValues) { 00841 $rows=array(); 00842 // Adding search field and button: 00843 $rows[]='<tr> 00844 <td nowrap><p>'.$this->pi_getLL("form_searchFor").' </p></td> 00845 <td><input type="text" name="'.$this->prefixId.'[sword]" value="'.htmlspecialchars($this->conf["show."]["clearSearchBox"]?'':$this->piVars["sword"]).'"'.$this->pi_classParam("searchbox-sword").'> <input type="submit" name="'.$this->prefixId.'[submit_button]" value="'.$this->pi_getLL("submit_button_label").'"'.$this->pi_classParam("searchbox-button").'></td> 00846 </tr>'; 00847 00848 if ($this->conf["show."]["clearSearchBox"] && $this->conf["show."]["clearSearchBox."]['enableSubSearchCheckBox']) { 00849 $rows[]='<tr> 00850 <td></td> 00851 <td><input type="hidden" name="'.$this->prefixId.'[sword_prev]" value="'.htmlspecialchars($this->piVars["sword"]).'"><input type="checkbox" name="'.$this->prefixId.'[sword_prev_include]" value="1"'.($this->piVars['sword_prev_include']?' checked="checked"':'').'> Add to current search words</td> 00852 </tr>'; 00853 } 00854 00855 00856 if ($this->piVars["ext"]) { 00857 if (is_array($optValues["type"]) || is_array($optValues["defOp"])) $rows[]='<tr> 00858 <td nowrap><p>'.$this->pi_getLL("form_match").' </p></td> 00859 <td>'.$this->renderSelectBox($this->prefixId.'[type]',$this->piVars["type"],$optValues["type"]). 00860 $this->renderSelectBox($this->prefixId.'[defOp]',$this->piVars["defOp"],$optValues["defOp"]).'</td> 00861 </tr>'; 00862 if (is_array($optValues["media"]) || is_array($optValues["lang"])) $rows[]='<tr> 00863 <td nowrap><p>'.$this->pi_getLL("form_searchIn").' </p></td> 00864 <td>'.$this->renderSelectBox($this->prefixId.'[media]',$this->piVars["media"],$optValues["media"]). 00865 $this->renderSelectBox($this->prefixId.'[lang]',$this->piVars["lang"],$optValues["lang"]).'</td> 00866 </tr>'; 00867 if (is_array($optValues["sections"])) $rows[]='<tr> 00868 <td nowrap><p>'.$this->pi_getLL("form_fromSection").' </p></td> 00869 <td>'.$this->renderSelectBox($this->prefixId.'[sections]',$this->piVars["sections"],$optValues["sections"]).'</td> 00870 </tr>'; 00871 if (is_array($optValues["order"]) || is_array($optValues["desc"]) || is_array($optValues["results"])) $rows[]='<tr> 00872 <td nowrap><p>'.$this->pi_getLL("form_orderBy").' </p></td> 00873 <td><p>'.$this->renderSelectBox($this->prefixId.'[order]',$this->piVars["order"],$optValues["order"]). 00874 $this->renderSelectBox($this->prefixId.'[desc]',$this->piVars["desc"],$optValues["desc"]). 00875 $this->renderSelectBox($this->prefixId.'[results]',$this->piVars["results"],$optValues["results"]).' '.$this->pi_getLL("form_atATime").'</p></td> 00876 </tr>'; 00877 if (is_array($optValues["group"]) || !$this->conf["blind."]["extResume"]) $rows[]='<tr> 00878 <td nowrap><p>'.$this->pi_getLL("form_style").' </p></td> 00879 <td><p>'.$this->renderSelectBox($this->prefixId.'[group]',$this->piVars["group"],$optValues["group"]). 00880 (!$this->conf["blind."]["extResume"] ? ' 00881 <input type="hidden" name="'.$this->prefixId.'[extResume]" value="0"><input type="checkbox" value="1" name="'.$this->prefixId.'[extResume]"'.($this->piVars["extResume"]?" CHECKED":"").'>'.$this->pi_getLL("form_extResume"):'').'</p></td> 00882 </tr>'; 00883 } 00884 00885 #debug(array($GLOBALS["TSFE"]->id,$GLOBALS["TSFE"]->sPre,$this->pi_getPageLink($GLOBALS["TSFE"]->id,$GLOBALS["TSFE"]->sPre))); 00886 00887 $out='<table '.$this->conf["tableParams."]["searchBox"].'> 00888 <form action="'.$this->pi_getPageLink($GLOBALS["TSFE"]->id,$GLOBALS["TSFE"]->sPre).'" method="POST" name="'.$this->prefixId.'"> 00889 '.implode(chr(10),$rows).' 00890 <input type="hidden" name="'.$this->prefixId.'[_sections]" value="0"> 00891 <input type="hidden" name="'.$this->prefixId.'[pointer]" value="0"> 00892 <input type="hidden" name="'.$this->prefixId.'[ext]" value="'.($this->piVars["ext"]?1:0).'"> 00893 </form> 00894 </table>'; 00895 $out.='<p>'. 00896 ($this->piVars["ext"] ? 00897 '<a href="'.$this->pi_getPageLink($GLOBALS["TSFE"]->id,$GLOBALS["TSFE"]->sPre,array($this->prefixId."[ext]"=>0)).'">'.$this->pi_getLL("link_regularSearch").'</a>' : 00898 '<a href="'.$this->pi_getPageLink($GLOBALS["TSFE"]->id,$GLOBALS["TSFE"]->sPre,array($this->prefixId."[ext]"=>1)).'">'.$this->pi_getLL("link_advancedSearch").'</a>' 00899 ).'</p>'; 00900 00901 return '<div'.$this->pi_classParam("searchbox").'>'.$out.'</div>'; 00902 } 00903 00907 function printRules() { 00908 $out = ""; 00909 if ($this->conf["show."]["rules"]) { 00910 $out = '<h2>'.$this->pi_getLL("rules_header").'</h2><p>'.nl2br(htmlspecialchars(trim($this->pi_getLL("rules_text")))).'</p>'; 00911 $out = '<div'.$this->pi_classParam("rules").'>'.$this->cObj->stdWrap($out, $this->conf["rules_stdWrap."]).'</div>'; 00912 } 00913 return $out; 00914 } 00915 00919 function printResultSectionLinks() { 00920 $lines=array(); 00921 reset($this->resultSections); 00922 while(list($id,$dat)=each($this->resultSections)) { 00923 $lines[]='<li><a href="'.$this->anchorPrefix.'#'.md5($id).'">'.(trim($dat[0])?htmlspecialchars(trim($dat[0])):$this->pi_getLL("unnamedSection")).' ('.$dat[1].' '.$this->pi_getLL("word_page".($dat[1]>1?"s":"")).')</a></li>'; 00924 } 00925 $out = '<ul>'.implode(chr(10),$lines).'</ul>'; 00926 return '<div'.$this->pi_classParam("sectionlinks").'>'.$this->cObj->stdWrap($out, $this->conf["sectionlinks_stdWrap."]).'</div>'; 00927 } 00928 00932 function makePointerSelector($count,$displayCount,$pointer) { 00933 $lines=array(); 00934 00935 // Previous pointer: 00936 if ($pointer>0) { 00937 $lines[]=$this->makePointerSelector_link("PREV",0); 00938 } 00939 00940 // 1-2-3 00941 for ($a=0;$a<t3lib_div::intInRange(ceil($count/$displayCount),1,10);$a++) { 00942 # $str = ($a*$displayCount+1)."-".(($a+1)*$displayCount); 00943 $str = $a+1; 00944 $linkStr = $this->makePointerSelector_link($str,$a); 00945 $lines[]= $pointer==$a ? '<strong>['.$linkStr.']</strong>' : $linkStr; 00946 } 00947 00948 // Next pointer: 00949 if ($pointer+1<ceil($count/$displayCount)) $lines[]=$this->makePointerSelector_link("NEXT",$pointer+1); 00950 00951 return implode(" - ",$lines); 00952 } 00953 00958 function makePointerSelector_link($str,$p) { 00959 return '<a href="#" onClick="document.'.$this->prefixId.'[\''.$this->prefixId.'[pointer]\'].value=\''.$p.'\';document.'.$this->prefixId.'.submit();return false;">'.$str.'</a>'; 00960 } 00961 00965 function tellUsWhatIsSeachedFor($sWArr) { 00966 reset($sWArr); 00967 $searchingFor=""; 00968 $c=0; 00969 while(list($k,$v)=each($sWArr)) { 00970 if ($c) { 00971 switch($v["oper"]) { 00972 case "OR": 00973 $searchingFor.=" ".$this->pi_getLL("searchFor_or")." ".$this->wrapSW($v["sword"]); 00974 break; 00975 case "AND NOT": 00976 $searchingFor.=" ".$this->pi_getLL("searchFor_butNot")." ".$this->wrapSW($v["sword"]); 00977 break; 00978 default: // AND... 00979 $searchingFor.=" ".$this->pi_getLL("searchFor_and")." ".$this->wrapSW($v["sword"]); 00980 break; 00981 } 00982 00983 } else { 00984 $searchingFor=$this->pi_getLL("searchFor")." ".$this->wrapSW($v["sword"]); 00985 } 00986 $c++; 00987 } 00988 return $searchingFor; 00989 } 00990 00994 function wrapSW($str) { 00995 return "'<span".$this->pi_classParam("sw").">".htmlspecialchars($str)."</span>'"; 00996 } 00997 01001 function renderSelectBox($name,$value,$optValues) { 01002 if (is_array($optValues)) { 01003 $opt=array(); 01004 $isSelFlag=0; 01005 reset($optValues); 01006 while(list($k,$v)=each($optValues)) { 01007 $sel = (!strcmp($k,$value)?" SELECTED":""); 01008 if ($sel) $isSelFlag++; 01009 $opt[]='<option value="'.htmlspecialchars($k).'"'.$sel.'>'.htmlspecialchars($v).'</option>'; 01010 } 01011 # if (!$isSelFlag && strcmp("",$value)) $opt[]='<option value="'.$value.'" SELECTED>'.htmlspecialchars("CURRENT VALUE '".$value."' DID NOT EXIST AMONG THE OPTIONS").'</option>'; 01012 return '<select name="'.$name.'">'.implode("",$opt).'</select>'; 01013 } 01014 } 01015 01016 01017 01018 01019 01020 01021 01022 01023 01024 01025 01026 01027 01028 01029 01030 /*********************************** 01031 01032 Result row LAYOUT 01033 01034 ***********************************/ 01035 01040 function compileResult($resultRows) { 01041 $content=""; 01042 01043 $newResultRows=array(); 01044 reset($resultRows); 01045 while(list(,$row)=each($resultRows)) { 01046 $id = md5($row["phash_grouping"]); 01047 if (is_array($newResultRows[$id])) { 01048 if (!$newResultRows[$id]["show_resume"] && $row["show_resume"]) { // swapping: 01049 // Remove old 01050 $subrows = $newResultRows[$id]["_sub"]; 01051 unset($newResultRows[$id]["_sub"]); 01052 $subrows[] =$newResultRows[$id]; 01053 01054 // Insert new: 01055 $newResultRows[$id]=$row; 01056 $newResultRows[$id]["_sub"]=$subrows; 01057 } else $newResultRows[$id]["_sub"][]=$row; 01058 } else { 01059 $newResultRows[$id]=$row; 01060 } 01061 } 01062 $resultRows=$newResultRows; 01063 01064 01065 switch($this->piVars["group"]) { 01066 case "sections": 01067 $rl2flag = substr($this->piVars["sections"],0,2)=="rl"; 01068 $sections=array(); 01069 reset($resultRows); 01070 while(list(,$row)=each($resultRows)) { 01071 $id = $row["rl0"]."-".$row["rl1"].($rl2flag?"-".$row["rl2"]:""); 01072 $sections[$id][]=$row; 01073 } 01074 01075 $this->resultSections=array(); 01076 reset($sections); 01077 while(list($id,$resultRows)=each($sections)) { 01078 $rlParts = explode("-",$id); 01079 01080 $theId = $rlParts[2]?$rlParts[2]:($rlParts[1]?$rlParts[1]:$rlParts[0]); 01081 $theRLid = $rlParts[2]?"rl2_".$rlParts[2]:($rlParts[1]?"rl1_".$rlParts[1]:"0"); 01082 01083 $sectionName = substr($this->getPathFromPageId($theId),1); 01084 if (!trim($sectionName)) { 01085 $sectionTitleLinked=$this->pi_getLL("unnamedSection").":"; 01086 } else { 01087 $sectionTitleLinked = '<a href="#" onClick="document.'.$this->prefixId.'[\''.$this->prefixId.'[_sections]\'].value=\''.$theRLid.'\';document.'.$this->prefixId.'.submit();return false;">'.$sectionName.':</a>'; 01088 } 01089 01090 $content.=$this->makeSectionHeader($id,$sectionTitleLinked,count($resultRows)); 01091 $this->resultSections[$id] = array($sectionName,count($resultRows)); 01092 reset($resultRows); 01093 while(list(,$row)=each($resultRows)) { 01094 $content.=$this->printResultRow($row); 01095 } 01096 } 01097 break; 01098 default: // flat: 01099 reset($resultRows); 01100 while(list(,$row)=each($resultRows)) { 01101 $content.=$this->printResultRow($row); 01102 } 01103 break; 01104 } 01105 return '<div'.$this->pi_classParam("res").'>'.$content.'</div>'; 01106 } 01107 01111 function makeSectionHeader($id,$sectionTitleLinked,$countResultRows) { 01112 return '<div'.$this->pi_classParam("secHead").'><a name="'.md5($id).'"></a><table '.$this->conf["tableParams."]["secHead"].'> 01113 <tr> 01114 <td width="95%"><h2>'.$sectionTitleLinked.'</h2></td> 01115 <td align="right" nowrap><p>'.$countResultRows.' '.$this->pi_getLL("word_page".($countResultRows>1?"s":"")).'</p></td> 01116 </tr> 01117 </table></div>'; 01118 } 01119 01123 function printResultRow($row,$headerOnly=0) { 01124 $specRowConf = $this->getSpecialConfigForRow($row); 01125 $CSSsuffix = $specRowConf["CSSsuffix"]?"-".$specRowConf["CSSsuffix"]:""; 01126 01127 // If external media, link to the media-file instead. 01128 if ($row["item_type"]) { 01129 if ($row["show_resume"]) { // Can link directly. 01130 $title = '<a href="'.$row["data_filename"].'">'.$row["result_number"].": ".$this->makeTitle($row).'</a>'; 01131 } else { // Suspicious, so linking to page instead... 01132 $copy_row=$row; 01133 unset($copy_row["cHashParams"]); 01134 $title = $this->linkPage($row["page_id"],$row["result_number"].": ".$this->makeTitle($row),$copy_row); 01135 } 01136 } else { // Else the page: 01137 $title = $this->linkPage($row["data_page_id"],$row["result_number"].": ".$this->makeTitle($row),$row); 01138 } 01139 01140 // Make the header row with title, icon and rating bar.: 01141 $out.='<tr '.$this->pi_classParam("title".$CSSsuffix).'> 01142 <td width="16">'.$this->makeItemTypeIcon($row["item_type"],"",$specRowConf).'</td> 01143 <td width="95%" nowrap><p>'.$title.'</p></td> 01144 <td nowrap><p'.$this->pi_classParam("percent".$CSSsuffix).'>'.$this->makeRating($row).'</p></td> 01145 </tr>'; 01146 01147 // Print the resume-section. If headerOnly is 1, then only the short resume is printed 01148 if (!$headerOnly) { 01149 $out.='<tr> 01150 <td></td> 01151 <td colspan=2'.$this->pi_classParam("descr".$CSSsuffix).'><p>'.$this->makeDescription($row,$this->piVars["extResume"]?0:1).'</p></td> 01152 </tr>'; 01153 $out.='<tr> 01154 <td></td> 01155 <td '.$this->pi_classParam("info".$CSSsuffix).' nowrap><p>'.$this->makeInfo($row).'</p></td> 01156 <td '.$this->pi_classParam("info".$CSSsuffix).' align="right"><p>'.$this->makeAccessIndication($row["page_id"]).''.$this->makeLanguageIndication($row).'</p></td> 01157 </tr>'; 01158 } elseif ($headerOnly==1) { 01159 $out.='<tr> 01160 <td></td> 01161 <td colspan=2'.$this->pi_classParam("descr".$CSSsuffix).'><p>'.$this->makeDescription($row,1,180).'</p></td> 01162 </tr>'; 01163 } elseif ($headerOnly==2) { 01164 // nothing. 01165 } 01166 01167 // If there are subrows (eg. subpages in a PDF-file or if a duplicate page is selected due to user-login (phash_grouping)) 01168 if (is_array($row["_sub"])) { 01169 if ($row["item_type"]==2) { 01170 $out.='<tr> 01171 <td></td> 01172 <td colspan=2><p><BR>'.$this->pi_getLL("res_otherMatching").'<BR><BR></p></td> 01173 </tr>'; 01174 01175 reset($row["_sub"]); 01176 while(list(,$subRow)=each($row["_sub"])) { 01177 $out.='<tr> 01178 <td></td> 01179 <td colspan=2><p>'.$this->printResultRow($subRow,1).'</p></td> 01180 </tr>'; 01181 } 01182 } else { 01183 $out.='<tr> 01184 <td></td> 01185 <td colspan=2><p>'.$this->pi_getLL("res_otherPageAsWell").'</p></td> 01186 </tr>'; 01187 } 01188 } 01189 01190 return '<table '.$this->conf["tableParams."]["searchRes"].'>'.$out.'</table><BR>'; 01191 } 01192 01196 function makeItemTypeIcon($it,$alt="",$specRowConf=array()) { 01197 $spec_flag = 0; 01198 01199 switch($it) { 01200 case 1: 01201 $icon="html.gif"; 01202 break; 01203 case 2: 01204 $icon="pdf.gif"; 01205 break; 01206 case 3: 01207 $icon="doc.gif"; 01208 break; 01209 case 4: 01210 $icon="txt.gif"; 01211 break; 01212 default: 01213 $icon="pages.gif"; 01214 if (is_array($specRowConf["pageIcon."])) { 01215 $spec_flag = 1; 01216 $spec = $this->cObj->IMAGE($specRowConf["pageIcon."]); 01217 } 01218 break; 01219 } 01220 if ($spec_flag) { 01221 return $spec; 01222 } else { 01223 $fullPath = t3lib_extMgm::siteRelPath("indexed_search").'pi/res/'.$icon; 01224 $info = @getimagesize(PATH_site.$fullPath); 01225 01226 return is_array($info) ? '<img src="'.$fullPath.'" hspace=3 '.$info[3].' title="'.htmlspecialchars($alt).'">' : ''; 01227 } 01228 } 01229 01233 function makeRating($row) { 01234 /* 01235 "rank_flag" => "Rang, prioritet", 01236 "rank_freq" => "Rang, frekvens", 01237 "rank_first" => "Rang, tæt på toppen", 01238 "rank_count" => "Rang, antal forekomster", 01239 "rating" => "Side-rating", 01240 "hits" => "Side-hits", 01241 "title" => "Dokumenttitel", 01242 "crdate" => "Oprettelsesdato", 01243 "mtime" => "Ændringsdato", 01244 */ 01245 switch((string)$this->piVars["order"]) { 01246 case "rank_count": 01247 return $row["order_val"]." matches"; 01248 break; 01249 case "rank_first": 01250 return ceil(t3lib_div::intInRange(255-$row["order_val"],1,255)/255*100)."%"; 01251 break; 01252 case "rank_flag": 01253 if ($this->firstRow["order_val2"]) { 01254 $base=$row["order_val1"]*256; // (3 MSB bit, 224 is highest value of order_val1 currently) 01255 $freqNumber = $row["order_val2"]/$this->firstRow["order_val2"]*pow(2,13); // 16-3 MSB = 13 01256 $total = t3lib_div::intInRange($base+$freqNumber,0,65536); 01257 #debug($total); 01258 #debug(log($total)/log(65536)*100,1); 01259 return ceil(log($total)/log(65536)*100)."%"; 01260 } 01261 break; 01262 case "rank_freq": 01263 $max = 10000; 01264 $total = t3lib_div::intInRange($row["order_val"],0,$max); 01265 # debug($total); 01266 return ceil(log($total)/log($max)*100)."%"; 01267 break; 01268 case "crdate": 01269 return $this->cObj->calcAge(time()-$row["item_crdate"],0); // ,$conf["age"] 01270 break; 01271 case "mtime": 01272 return $this->cObj->calcAge(time()-$row["item_mtime"],0); // ,$conf["age"] 01273 break; 01274 default: // fx. title 01275 # return "[rating...]"; 01276 return " "; 01277 break; 01278 } 01279 } 01280 01285 function makeDescription($row,$noMarkup=0,$lgd=180) { 01286 if ($row["show_resume"]) { 01287 #debug($row) ; 01288 if (!$noMarkup) { 01289 $markedSW = ""; 01290 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', 'index_fulltext', 'phash='.intval($row['phash'])); 01291 if ($ftdrow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { 01292 $markedSW = $this->markupSWpartsOfString($ftdrow["fulltextdata"]); 01293 } 01294 $GLOBALS['TYPO3_DB']->sql_free_result($res); 01295 } 01296 01297 return htmlspecialchars(t3lib_div::fixed_lgd(str_replace(" "," ",$row["item_description"]),$lgd)).(trim($markedSW)?" ... ".$markedSW:"").' 01298 <BR><img src=clear.gif width=1 height=5>'; 01299 } else { 01300 return '<font color="#666666">'.$this->pi_getLL("res_noResume").' 01301 <BR><img src=clear.gif width=1 height=5></font>'; 01302 } 01303 } 01304 01308 function markupSWpartsOfString($str) { 01309 $str = str_replace(" "," ",t3lib_parsehtml::bidir_htmlspecialchars($str,-1)); 01310 reset($this->sWArr); 01311 #debug($this->sWArr); 01312 $swForReg=array(); 01313 while(list(,$d)=each($this->sWArr)) { 01314 $swForReg[]=quotemeta($d["sword"]); 01315 } 01316 $regExString = implode("|",$swForReg); 01317 01318 $noAlph="[^[:alnum:]]"; 01319 $theType = (string)$this->piVars["type"]; 01320 switch($theType) { 01321 case "1": 01322 case "20": 01323 // Nothing... 01324 break; 01325 case "2": 01326 $regExString = $noAlph."(".$regExString.")"; 01327 break; 01328 case "3": 01329 $regExString = "(".$regExString.")".$noAlph; 01330 break; 01331 case "10": 01332 break; 01333 default: 01334 $regExString = $noAlph."(".$regExString.")".$noAlph; 01335 break; 01336 } 01337 #debug(array($regExString)); 01338 01339 $parts = spliti($regExString,$str,6); 01340 #debug($parts); 01341 reset($parts); 01342 $strLen=0; 01343 01344 $snippets=array(); 01345 while(list($k,$strP)=each($parts)) { 01346 if ($k+1<count($parts)) { 01347 $strLen+=strlen($strP); 01348 $reg=array(); 01349 eregi("^".$regExString,substr($str,$strLen,50),$reg); 01350 01351 $snippets[] = array( 01352 # substr($str,$strLen-50,50), 01353 substr($parts[$k],-50), 01354 substr($str,$strLen,strlen($reg[0])), 01355 substr($parts[$k+1],0,50) 01356 # substr($str,$strLen+strlen($reg[0]),50), 01357 ); 01358 01359 $strLen+=strlen($reg[0]); 01360 # debug($reg); 01361 } 01362 } 01363 01364 reset($snippets); 01365 $content=array(); 01366 while(list(,$d)=each($snippets)) { 01367 $content[]=htmlspecialchars($d[0]).'<span'.$this->pi_classParam("redMarkup").'>'.htmlspecialchars($d[1]).'</span>'.htmlspecialchars($d[2]); 01368 } 01369 01370 return implode(" ... ",$content); 01371 } 01372 01376 function makeTitle($row) { 01377 $add=""; 01378 if ($row["item_type"]==2) { 01379 $dat = unserialize($row["cHashParams"]); 01380 $pp=explode("-",$dat["key"]); 01381 if ($pp[0]!=$pp[1]) { 01382 $add=", pages ".$dat["key"]; 01383 } else $add=", page ".$pp[0]; 01384 } 01385 return htmlspecialchars(t3lib_div::fixed_lgd($row["item_title"],50)).$add; 01386 } 01387 01391 function makeInfo($row) { 01392 $lines=array(); 01393 $lines[]=$this->pi_getLL("res_size")." ".t3lib_div::formatSize($row["item_size"]).""; 01394 $lines[]=$this->pi_getLL("res_created")." ".date("d-m-y",$row["item_crdate"]).""; 01395 $lines[]=$this->pi_getLL("res_modified")." ".date("d-m-y H:i",$row["item_mtime"]).""; 01396 $out = implode(" - ",$lines); 01397 $pathId = $row["data_page_id"]?$row["data_page_id"]:$row["page_id"]; 01398 $pathMP = $row["data_page_id"]?$row["data_page_mp"]:""; 01399 $out.="<BR>".$this->pi_getLL("res_path")." ".$this->linkPage($pathId,htmlspecialchars($this->getPathFromPageId($pathId,$pathMP))); 01400 return $out; 01401 } 01402 01406 function getSpecialConfigForRow($row) { 01407 $pathId = $row["data_page_id"]?$row["data_page_id"]:$row["page_id"]; 01408 $pathMP = $row["data_page_id"]?$row["data_page_mp"]:""; 01409 01410 $rl = $this->getRootLine($pathId,$pathMP); 01411 $specConf = $this->conf["specConfs."]["0."]; 01412 if (is_array($rl)) { 01413 reset($rl); 01414 while(list(,$dat)=each($rl)) { 01415 if (is_array($this->conf["specConfs."][$dat["uid"]."."])) { 01416 $specConf = $this->conf["specConfs."][$dat["uid"]."."]; 01417 break; 01418 } 01419 } 01420 } 01421 01422 return $specConf; 01423 } 01424 01428 function makeLanguageIndication($row) { 01429 if ($row["item_type"]==0) { 01430 switch($row["sys_language_uid"]) { 01431 // OBVIOUSLY this will not work generally. First we need to know WHICH flag is used for WHICH language-uid. This just shows the concept works. 01432 case 1: 01433 return '<img src="tslib/media/flags/flag_dk.gif" width="21" height="13" border="0" alt="Danish">'; 01434 break; 01435 case 2: 01436 return '<img src="tslib/media/flags/flag_de.gif" width="21" height="13" border="0" alt="German">'; 01437 break; 01438 default: 01439 # return '<img src="tslib/media/flags/flag_uk.gif" width="21" height="13" border="0" alt="English - default">'; 01440 break; 01441 } 01442 } 01443 return " "; 01444 } 01445 01449 function makeAccessIndication($id) { 01450 if (is_array($this->fe_groups_required[$id]) && count($this->fe_groups_required[$id])) { 01451 return '<img src="'.t3lib_extMgm::siteRelPath("indexed_search").'pi/res/locked.gif" width="12" height="15" vspace=5 title="'.sprintf($this->pi_getLL("res_memberGroups"),implode(",",array_unique($this->fe_groups_required[$id]))).'">'; 01452 } 01453 } 01454 01458 function linkPage($id,$str,$row=array()) { 01459 # return $str; 01460 $urlParameters = unserialize($row["cHashParams"]); 01461 # $urlParameters["cHash"] = t3lib_div::shortMD5(serialize($row["cHashParams"])); // not needed - it's there already... 01462 #debug($urlParameters); 01463 01464 01465 // This will make sure that the path is retrieved if it hasn't been already. Used only for the sake of the domain_record thing... 01466 if (!is_array($this->domain_records[$id])) { 01467 $this->getPathFromPageId($id); 01468 } 01469 01470 // If external domain, then link to that: 01471 if (count($this->domain_records[$id])) { 01472 reset($this->domain_records[$id]); 01473 $firstDom = current($this->domain_records[$id]); 01474 $scheme = t3lib_div::getIndpEnv('TYPO3_SSL') ? 'https://' : 'http://'; 01475 01476 $addParams=""; 01477 if (is_array($urlParameters)) { 01478 if (count($urlParameters)) { 01479 reset($urlParameters); 01480 while(list($k,$v)=each($urlParameters)) { 01481 $addParams.="&".$k."=".rawurlencode($v); 01482 } 01483 } 01484 } 01485 01486 return '<a href="'.$scheme.$firstDom.'/index.php?id='.$id.$addParams.'" target="'.$this->conf["search."]["detect_sys_domain_records."]["target"].'">'.htmlspecialchars($str).'</a>'; 01487 } else { 01488 return $this->pi_linkToPage($str,$id,$this->conf["result_link_target"],$urlParameters); 01489 } 01490 } 01491 01495 function getRootLine($id,$pathMP="") { 01496 $identStr = $id."|".$pathMP; 01497 01498 if (!isset($this->cache_path[$identStr])) { 01499 $this->cache_rl[$identStr] = $GLOBALS["TSFE"]->sys_page->getRootLine($id,$pathMP); 01500 } 01501 return $this->cache_rl[$identStr]; 01502 } 01503 01507 function getFirstSysDomainRecordForPage($id) { 01508 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('domainName', 'sys_domain', 'pid='.intval($id).$this->cObj->enableFields('sys_domain'), '', 'sorting'); 01509 $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res); 01510 return ereg_replace("\/$","",$row["domainName"]); 01511 } 01512 01516 function getPathFromPageId($id,$pathMP="") { 01517 // Here I imagine some caching... 01518 $identStr = $id."|".$pathMP; 01519 01520 if (!isset($this->cache_path[$identStr])) { 01521 $this->fe_groups_required[$id]=array(); 01522 $this->domain_records[$id]=array(); 01523 $rl = $this->getRootLine($id,$pathMP); 01524 $hitRoot=0; 01525 $path=""; 01526 if (is_array($rl) && count($rl)) { 01527 reset($rl); 01528 while(list($k,$v)=each($rl)) { 01529 // Check fe_user 01530 if ($v["fe_group"] && ($v["uid"]==$id || $v["extendToSubpages"])) { 01531 $this->fe_groups_required[$id][]=$v["fe_group"]; 01532 } 01533 // Check sys_domain. 01534 if ($this->conf["search."]["detect_sys_domain_records"]) { 01535 $sysDName = $this->getFirstSysDomainRecordForPage($v["uid"]); 01536 if ($sysDName) { 01537 $this->domain_records[$id][]=$sysDName; 01538 # debug($sysDName); 01539 // Set path accordingly: 01540 $path=$sysDName.$path; 01541 break; 01542 } 01543 } 01544 01545 // Stop, if we find that the current id is the current root page. 01546 if ($v["uid"]==$GLOBALS["TSFE"]->config["rootLine"][0]["uid"]) { 01547 break; 01548 } 01549 $path="/".$v["title"].$path; 01550 } 01551 } 01552 01553 # $pageRec = $GLOBALS["TSFE"]->sys_page->checkRecord("pages",$id); 01554 # $path.="/".$pageRec["title"]; 01555 $this->cache_path[$identStr] = $path; 01556 01557 if (is_array($this->conf["path_stdWrap."])) { 01558 $this->cache_path[$identStr] = $this->cObj->stdWrap($this->cache_path[$identStr], $this->conf["path_stdWrap."]); 01559 } 01560 } 01561 01562 return $this->cache_path[$identStr]; 01563 } 01564 01568 function pi_list_browseresults($showResultCount=1,$addString="",$addPart="") { 01569 01570 // Initializing variables: 01571 $pointer=$this->piVars["pointer"]; 01572 $count=$this->internal["res_count"]; 01573 $results_at_a_time = t3lib_div::intInRange($this->internal["results_at_a_time"],1,1000); 01574 $maxPages = t3lib_div::intInRange($this->internal["maxPages"],1,100); 01575 $max = t3lib_div::intInRange(ceil($count/$results_at_a_time),1,$maxPages); 01576 $pointer=intval($pointer); 01577 $links=array(); 01578 01579 // Make browse-table/links: 01580 if ($pointer>0) { 01581 $links[]='<td><p>'.$this->makePointerSelector_link($this->pi_getLL("pi_list_browseresults_prev","< Previous"),$pointer-1).'</p></td>'; 01582 } 01583 for($a=0;$a<$max;$a++) { 01584 $links[]='<td'.($pointer==$a?$this->pi_classParam("browsebox-SCell"):"").'><p>'.$this->makePointerSelector_link(trim($this->pi_getLL("pi_list_browseresults_page","Page")." ".($a+1)),$a).'</p></td>'; 01585 } 01586 if ($pointer<ceil($count/$results_at_a_time)-1) { 01587 $links[]='<td><p>'.$this->makePointerSelector_link($this->pi_getLL("pi_list_browseresults_next","Next >"),$pointer+1).'</p></td>'; 01588 } 01589 01590 $pR1 = $pointer*$results_at_a_time+1; 01591 $pR2 = $pointer*$results_at_a_time+$results_at_a_time; 01592 $sTables = '<DIV'.$this->pi_classParam("browsebox").'>'. 01593 ($showResultCount ? '<P>'.sprintf( 01594 str_replace("###SPAN_BEGIN###","<span".$this->pi_classParam("browsebox-strong").">",$this->pi_getLL("pi_list_browseresults_displays","Displaying results ###SPAN_BEGIN###%s to %s</span> out of ###SPAN_BEGIN###%s</span>")), 01595 $pR1, 01596 min(array($this->internal["res_count"],$pR2)), 01597 $this->internal["res_count"] 01598 ).$addString.'</P>':'' 01599 ).$addPart. 01600 '<table> 01601 <tr>'.implode("",$links).'</tr> 01602 </table></DIV>'; 01603 return $sTables; 01604 } 01605 01609 function getMenu($id) { 01610 if ($this->conf["show."]["LxALLtypes"]) { 01611 $output = Array(); 01612 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('title,uid', 'pages', 'pid='.intval($id).$this->cObj->enableFields('pages'), '', 'sorting'); 01613 while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { 01614 $output[$row["uid"]]=$GLOBALS["TSFE"]->sys_page->getPageOverlay($row); 01615 } 01616 return $output; 01617 } else { 01618 return $GLOBALS["TSFE"]->sys_page->getMenu($id); 01619 } 01620 } 01621 } 01622 01623 if (defined("TYPO3_MODE") && $TYPO3_CONF_VARS[TYPO3_MODE]["XCLASS"]["ext/indexed_search/pi/class.tx_indexedsearch.php"]) { 01624 include_once($TYPO3_CONF_VARS[TYPO3_MODE]["XCLASS"]["ext/indexed_search/pi/class.tx_indexedsearch.php"]); 01625 } 01626 01627 ?>