Documentation TYPO3 par Ameos |
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) 2001-2005 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 ***************************************************************/ 00106 require_once(PATH_tslib.'class.tslib_pibase.php'); 00107 require_once(PATH_tslib.'class.tslib_search.php'); 00108 require_once(t3lib_extMgm::extPath('indexed_search').'class.indexer.php'); 00109 00110 00121 class tx_indexedsearch extends tslib_pibase { 00122 var $prefixId = 'tx_indexedsearch'; // Same as class name 00123 var $scriptRelPath = 'pi/class.tx_indexedsearch.php'; // Path to this script relative to the extension dir. 00124 var $extKey = 'indexed_search'; // The extension key. 00125 00126 var $join_pages = 0; // See document for info about this flag... 00127 var $defaultResultNumber = 20; 00128 00129 var $operator_translate_table = Array ( // case-sensitive. Defines the words, which will be operators between words 00130 Array ('+' , 'AND'), 00131 Array ('|' , 'OR'), 00132 Array ('-' , 'AND NOT'), 00133 // english 00134 # Array ('AND' , 'AND'), 00135 # Array ('OR' , 'OR'), 00136 # Array ('NOT' , 'AND NOT'), 00137 ); 00138 00139 // Internal variable 00140 var $wholeSiteIdList = 0; // Root-page PIDs to search in (rl0 field where clause, see initialize() function) 00141 00142 // Internals: 00143 var $sWArr = array(); // Search Words and operators 00144 var $optValues = array(); // Selector box values for search configuration form 00145 var $firstRow = Array(); // Will hold the first row in result - used to calculate relative hit-ratings. 00146 00147 var $cache_path = array(); // Caching of page path 00148 var $cache_rl = array(); // Caching of root line data 00149 var $fe_groups_required = array(); // Required fe_groups memberships for display of a result. 00150 var $domain_records = array(); // Domain records (?) 00151 var $wSelClauses = array(); // Select clauses for individual words 00152 var $resultSections = array(); // Page tree sections for search result. 00153 var $external_parsers = array(); // External parser objects 00154 var $iconFileNameCache = array(); // Storage of icons.... 00155 var $lexerObj; // Lexer object 00156 00157 00165 function main($content, $conf) { 00166 00167 // Initialize: 00168 $this->conf = $conf; 00169 $this->pi_loadLL(); 00170 $this->pi_setPiVarDefaults(); 00171 00172 // Initialize the indexer-class - just to use a few function (for making hashes) 00173 $this->indexerObj = t3lib_div::makeInstance('tx_indexedsearch_indexer'); 00174 00175 // Initialize: 00176 $this->initialize(); 00177 00178 // Do search: 00179 // If there were any search words entered... 00180 if (is_array($this->sWArr)) { 00181 $content = $this->doSearch($this->sWArr); 00182 } 00183 00184 // Finally compile all the content, form, messages and results: 00185 $content= 00186 $this->makeSearchForm($this->optValues). 00187 $this->printRules(). 00188 $content; 00189 00190 return $this->pi_wrapInBaseClass($content); 00191 } 00192 00198 function initialize() { 00199 global $TYPO3_CONF_VARS; 00200 00201 // Initialize external document parsers for icon display and other soft operations 00202 if (is_array($TYPO3_CONF_VARS['EXTCONF']['indexed_search']['external_parsers'])) { 00203 foreach($TYPO3_CONF_VARS['EXTCONF']['indexed_search']['external_parsers'] as $extension => $_objRef) { 00204 $this->external_parsers[$extension] = &t3lib_div::getUserObj($_objRef); 00205 00206 // Init parser and if it returns false, unset its entry again: 00207 if (!$this->external_parsers[$extension]->softInit($extension)) { 00208 unset($this->external_parsers[$extension]); 00209 } 00210 } 00211 } 00212 00213 // Init lexer (used to post-processing of search words) 00214 $lexerObjRef = $TYPO3_CONF_VARS['EXTCONF']['indexed_search']['lexer'] ? 00215 $TYPO3_CONF_VARS['EXTCONF']['indexed_search']['lexer'] : 00216 'EXT:indexed_search/class.lexer.php:&tx_indexedsearch_lexer'; 00217 $this->lexerObj = &t3lib_div::getUserObj($lexerObjRef); 00218 00219 // If "_sections" is set, this value overrides any existing value. 00220 if ($this->piVars['_sections']) $this->piVars['sections'] = $this->piVars['_sections']; 00221 00222 // Add previous search words to current 00223 if ($this->piVars['sword_prev_include'] && $this->piVars['sword_prev']) { 00224 $this->piVars['sword'] = trim($this->piVars['sword_prev']).' '.$this->piVars['sword']; 00225 } 00226 00227 $this->piVars['results'] = t3lib_div::intInRange($this->piVars['results'],1,100000,$this->defaultResultNumber); 00228 00229 // Selector-box values defined here: 00230 $this->optValues = Array( 00231 'type' => Array( 00232 '0' => $this->pi_getLL('opt_type_0'), 00233 '1' => $this->pi_getLL('opt_type_1'), 00234 '2' => $this->pi_getLL('opt_type_2'), 00235 '3' => $this->pi_getLL('opt_type_3'), 00236 '10' => $this->pi_getLL('opt_type_10'), 00237 '20' => $this->pi_getLL('opt_type_20'), 00238 ), 00239 'defOp' => Array( 00240 '0' => $this->pi_getLL('opt_defOp_0'), 00241 '1' => $this->pi_getLL('opt_defOp_1'), 00242 ), 00243 'sections' => Array( 00244 '0' => $this->pi_getLL('opt_sections_0'), 00245 '-1' => $this->pi_getLL('opt_sections_-1'), 00246 '-2' => $this->pi_getLL('opt_sections_-2'), 00247 '-3' => $this->pi_getLL('opt_sections_-3'), 00248 // 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. 00249 ), 00250 'media' => Array( 00251 '-1' => $this->pi_getLL('opt_media_-1'), 00252 '0' => $this->pi_getLL('opt_media_0'), 00253 '-2' => $this->pi_getLL('opt_media_-2'), 00254 ), 00255 'order' => Array( 00256 'rank_flag' => $this->pi_getLL('opt_order_rank_flag'), 00257 'rank_freq' => $this->pi_getLL('opt_order_rank_freq'), 00258 'rank_first' => $this->pi_getLL('opt_order_rank_first'), 00259 'rank_count' => $this->pi_getLL('opt_order_rank_count'), 00260 'mtime' => $this->pi_getLL('opt_order_mtime'), 00261 'title' => $this->pi_getLL('opt_order_title'), 00262 'crdate' => $this->pi_getLL('opt_order_crdate'), 00263 ), 00264 'group' => Array ( 00265 'sections' => $this->pi_getLL('opt_group_sections'), 00266 'flat' => $this->pi_getLL('opt_group_flat'), 00267 ), 00268 'lang' => Array ( 00269 -1 => $this->pi_getLL('opt_lang_-1'), 00270 0 => $this->pi_getLL('opt_lang_0'), 00271 ), 00272 'desc' => Array ( 00273 '0' => $this->pi_getLL('opt_desc_0'), 00274 '1' => $this->pi_getLL('opt_desc_1'), 00275 ), 00276 'results' => Array ( 00277 '10' => '10', 00278 '20' => '20', 00279 '50' => '50', 00280 '100' => '100', 00281 ) 00282 ); 00283 00284 // Add media to search in: 00285 foreach($this->external_parsers as $extension => $obj) { 00286 if ($name = $obj->searchTypeMediaTitle($extension)) { 00287 $this->optValues['media'][$extension] = $this->pi_getLL('opt_sections_'.$extension,$name); 00288 } 00289 } 00290 00291 // Add operators for various languages 00292 // Converts the operators to UTF-8 and lowercase 00293 $this->operator_translate_table[] = Array($GLOBALS['TSFE']->csConvObj->conv_case('utf-8',$GLOBALS['TSFE']->csConvObj->utf8_encode($this->pi_getLL('local_operator_AND'), $GLOBALS['TSFE']->renderCharset),'toLower') , 'AND'); 00294 $this->operator_translate_table[] = Array($GLOBALS['TSFE']->csConvObj->conv_case('utf-8',$GLOBALS['TSFE']->csConvObj->utf8_encode($this->pi_getLL('local_operator_OR'), $GLOBALS['TSFE']->renderCharset),'toLower') , 'OR'); 00295 $this->operator_translate_table[] = Array($GLOBALS['TSFE']->csConvObj->conv_case('utf-8',$GLOBALS['TSFE']->csConvObj->utf8_encode($this->pi_getLL('local_operator_NOT'), $GLOBALS['TSFE']->renderCharset),'toLower') , 'AND NOT'); 00296 00297 // This is the id of the site root. This value may be a commalist of integer (prepared for this) 00298 $this->wholeSiteIdList = intval($GLOBALS['TSFE']->config['rootLine'][0]['uid']); 00299 00300 // Creating levels for section menu: 00301 // This selects the first and secondary menus for the "sections" selector - so we can search in sections and sub sections. 00302 if ($this->conf['show.']['L1sections']) { 00303 $firstLevelMenu = $this->getMenu($this->wholeSiteIdList); 00304 while(list($kk,$mR) = each($firstLevelMenu)) { 00305 if ($mR['doktype']!=5) { 00306 $this->optValues['sections']['rl1_'.$mR['uid']] = trim($this->pi_getLL('opt_RL1').' '.$mR['title']); 00307 if ($this->conf['show.']['L2sections']) { 00308 $secondLevelMenu = $this->getMenu($mR['uid']); 00309 while(list($kk2,$mR2) = each($secondLevelMenu)) { 00310 if ($mR['doktype']!=5) { 00311 $this->optValues['sections']['rl2_'.$mR2['uid']] = trim($this->pi_getLL('opt_RL2').' '.$mR2['title']); 00312 } else unset($secondLevelMenu[$kk2]); 00313 } 00314 $this->optValues['sections']['rl2_'.implode(',',array_keys($secondLevelMenu))] = $this->pi_getLL('opt_RL2ALL'); 00315 } 00316 } else unset($firstLevelMenu[$kk]); 00317 } 00318 $this->optValues['sections']['rl1_'.implode(',',array_keys($firstLevelMenu))] = $this->pi_getLL('opt_RL1ALL'); 00319 } 00320 00321 // Setting the list of root PIDs for the search. Notice, these page IDs MUST have a TypoScript template with root flag on them! Basically this list is used to select on the "rl0" field and page ids are registered as "rl0" only if a TypoScript template record with root flag is there. 00322 // 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... 00323 if ($this->conf['search.']['rootPidList']) { 00324 $this->wholeSiteIdList = implode(',',t3lib_div::intExplode(',',$this->conf['search.']['rootPidList'])); 00325 } 00326 00327 // Add search languages: 00328 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', 'sys_language', '1=1'.$this->cObj->enableFields('sys_language')); 00329 while($lR = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { 00330 $this->optValues['lang'][$lR['uid']] = $lR['title']; 00331 } 00332 00333 // Calling hook for modification of initialized content 00334 if ($hookObj = &$this->hookRequest('initialize_postProc')) { 00335 $hookObj->initialize_postProc(); 00336 } 00337 00338 // Default values set: 00339 // Setting first values in optValues as default values IF there is not corresponding piVar value set already. 00340 foreach($this->optValues as $kk => $vv) { 00341 if (!isset($this->piVars[$kk])) { 00342 reset($vv); 00343 $this->piVars[$kk] = key($vv); 00344 } 00345 } 00346 00347 // Blind selectors: 00348 if (is_array($this->conf['blind.'])) { 00349 foreach($this->conf['blind.'] as $kk => $vv) { 00350 if (is_array($vv)) { 00351 foreach($vv as $kkk => $vvv) { 00352 if (!is_array($vvv) && $vvv && is_array($this->optValues[substr($kk,0,-1)])) { 00353 unset($this->optValues[substr($kk,0,-1)][$kkk]); 00354 } 00355 } 00356 } elseif ($vv) { // If value is not set, unset the option array. 00357 unset($this->optValues[$kk]); 00358 } 00359 } 00360 } 00361 00362 // This gets the search-words into the $sWArr: 00363 $this->sWArr = $this->getSearchWords($this->piVars['defOp']); 00364 } 00365 00381 function getSearchWords($defOp) { 00382 // Shorten search-word string to max 200 bytes (does NOT take multibyte charsets into account - but never mind, shortening the string here is only a run-away feature!) 00383 $inSW = substr($this->piVars['sword'],0,200); 00384 00385 // Convert to UTF-8 + conv. entities (was also converted during indexing!) 00386 $inSW = $GLOBALS['TSFE']->csConvObj->utf8_encode($inSW, $GLOBALS['TSFE']->metaCharset); 00387 $inSW = $GLOBALS['TSFE']->csConvObj->entities_to_utf8($inSW,TRUE); 00388 00389 if ($hookObj = &$this->hookRequest('getSearchWords')) { 00390 return $hookObj->getSearchWords_splitSWords($inSW, $defOp); 00391 } else { 00392 00393 if ($this->piVars['type']==20) { 00394 return array(array('sword'=>trim($inSW), 'oper'=>'AND')); 00395 } else { 00396 $search = t3lib_div::makeInstance('tslib_search'); 00397 $search->default_operator = $defOp==1 ? 'OR' : 'AND'; 00398 $search->operator_translate_table = $this->operator_translate_table; 00399 $search->register_and_explode_search_string($inSW); 00400 00401 if (is_array($search->sword_array)) { 00402 return $this->procSearchWordsByLexer($search->sword_array); 00403 } 00404 } 00405 } 00406 } 00407 00415 function procSearchWordsByLexer($SWArr) { 00416 00417 // Init output variable: 00418 $newSWArr = array(); 00419 00420 // Traverse the search word array: 00421 foreach($SWArr as $wordDef) { 00422 if (!strstr($wordDef['sword'],' ')) { // No space in word (otherwise it might be a sentense in quotes like "there is"). 00423 // SPlit the search word by lexer: 00424 $res = $this->lexerObj->split2Words($wordDef['sword']); 00425 00426 // Traverse lexer result and add all words again: 00427 foreach($res as $word) { 00428 $newSWArr[] = array('sword'=>$word, 'oper'=>$wordDef['oper']); 00429 } 00430 } else { 00431 $newSWArr[] = $wordDef; 00432 } 00433 } 00434 00435 // Return result: 00436 return $newSWArr; 00437 } 00438 00439 00440 00441 00442 00443 00444 00445 00446 00447 /***************************** 00448 * 00449 * Main functions 00450 * 00451 *****************************/ 00452 00459 function doSearch($sWArr) { 00460 00461 // Get result rows: 00462 $pt1 = t3lib_div::milliseconds(); 00463 if ($hookObj = &$this->hookRequest('getResultRows')) { 00464 $resData = $hookObj->getResultRows($sWArr); 00465 } else { 00466 $resData = $this->getResultRows($sWArr); 00467 } 00468 00469 // Display search results: 00470 $pt2 = t3lib_div::milliseconds(); 00471 if ($hookObj = &$this->hookRequest('getDisplayResults')) { 00472 $content = $hookObj->getDisplayResults($sWArr, $resData); 00473 } else { 00474 $content = $this->getDisplayResults($sWArr, $resData); 00475 } 00476 00477 $pt3 = t3lib_div::milliseconds(); 00478 00479 // Write search statistics 00480 $this->writeSearchStat($sWArr,$resData['count'],array($pt1,$pt2,$pt3)); 00481 00482 // Return content: 00483 return $content; 00484 } 00485 00492 function getResultRows($sWArr) { 00493 00494 // Getting SQL result pointer: 00495 $GLOBALS['TT']->push('Searching result'); 00496 $res = $this->getResultRows_SQLpointer($sWArr); 00497 $GLOBALS['TT']->pull(); 00498 00499 // Organize and process result: 00500 if ($res) { 00501 00502 // Get some variables: 00503 $count = $GLOBALS['TYPO3_DB']->sql_num_rows($res); 00504 $pointer = t3lib_div::intInRange($this->piVars['pointer'],0,floor($count/$this->piVars['results'])); 00505 00506 // Initialize result accumulation variables: 00507 $c = 0; 00508 $lines = Array(); 00509 $grouping_phashes = array(); // Used to filter out duplicates. 00510 $grouping_chashes = array(); // Used to filter out duplicates BASED ON cHash. 00511 $firstRow = Array(); // Will hold the first row in result - used to calculate relative hit-ratings. 00512 $resultRows = Array(); // Will hold the results rows for display. 00513 00514 // Now, traverse result and put the rows to be displayed into an array 00515 // Each row should be a the fields from 'ISEC.*, IP.*' combined + artificial fields "show_resume" (boolean) and "result_number" (counter) 00516 while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { 00517 00518 // Set first row: 00519 if (!$c) { 00520 $firstRow = $row; 00521 } 00522 00523 $row['show_resume'] = $this->checkResume($row); // Tells whether we can link directly to a document or not (depends on possible right problems) 00524 $phashGr = !in_array($row['phash_grouping'], $grouping_phashes); 00525 $chashGr = !in_array($row['contentHash'].'.'.$row['data_page_id'], $grouping_chashes); 00526 if ($phashGr && $chashGr) { 00527 if ($row['show_resume']) { // Only if the resume may be shown are we going to filter out duplicates... 00528 if (!$this->multiplePagesType($row['item_type'])) { // Only on documents which are not multiple pages documents 00529 $grouping_phashes[] = $row['phash_grouping']; 00530 } 00531 $grouping_chashes[] = $row['contentHash'].'.'.$row['data_page_id']; 00532 } 00533 $c++; 00534 00535 // All rows for display is put into resultRows[] 00536 if ($c > $pointer * $this->piVars['results']) { 00537 $row['result_number'] = $c; 00538 $resultRows[] = $row; 00539 if ($c+1 > ($pointer+1)*$this->piVars['results']) break; 00540 } 00541 } else { 00542 $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. 00543 } 00544 } 00545 00546 return array( 00547 'resultRows' => $resultRows, 00548 'firstRow' => $firstRow, 00549 'count' => $count 00550 ); 00551 } else { // No results found: 00552 return FALSE; 00553 } 00554 } 00555 00562 function getResultRows_SQLpointer($sWArr) { 00563 // This SEARCHES for the searchwords in $sWArr AND returns a COMPLETE list of phash-integers of the matches. 00564 $list = $this->getPhashList($sWArr); 00565 00566 // Perform SQL Search / collection of result rows array: 00567 if ($list) { 00568 // Do the search: 00569 return $this->execFinalQuery($list); 00570 } else { 00571 return FALSE; 00572 } 00573 } 00574 00582 function getDisplayResults($sWArr, $resData) { 00583 // Perform display of result rows array: 00584 if ($resData) { 00585 $GLOBALS['TT']->push('Display Final result'); 00586 00587 // Set first selected row (for calculation of ranking later) 00588 $this->firstRow = $resData['firstRow']; 00589 00590 // Result display here: 00591 $rowcontent = ''; 00592 $rowcontent.= $this->compileResult($resData['resultRows']); 00593 00594 // Browsing box: 00595 if ($resData['count']) { 00596 $this->internal['res_count'] = $resData['count']; 00597 $this->internal['results_at_a_time'] = $this->piVars['results']; 00598 $this->internal['maxPages'] = t3lib_div::intInRange($this->conf['search.']['page_links'],1,100,10); 00599 $addString = ($resData['count']&&$this->piVars['group']=='sections' ? ' '.sprintf($this->pi_getLL(count($this->resultSections)>1?'inNsections':'inNsection'),count($this->resultSections)):''); 00600 $browseBox1 = $this->pi_list_browseresults(1,$addString,$this->printResultSectionLinks()); 00601 $browseBox2 = $this->pi_list_browseresults(0); 00602 } 00603 00604 // Browsing nav, bottom. 00605 if ($resData['count']) { 00606 $content = $browseBox1.$rowcontent.$browseBox2; 00607 } else { 00608 $content = '<p'.$this->pi_classParam('noresults').'>'.$this->pi_getLL('noResults','',1).'</p>'; 00609 } 00610 00611 $GLOBALS['TT']->pull(); 00612 } else { 00613 $content.='<p'.$this->pi_classParam('noresults').'>'.$this->pi_getLL('noResults','',1).'</p>'; 00614 } 00615 00616 // Print a message telling which words we searched for, and in which sections etc. 00617 $what = $this->tellUsWhatIsSeachedFor($sWArr). 00618 (substr($this->piVars['sections'],0,2)=='rl'?' '.$this->pi_getLL('inSection','',1).' "'.substr($this->getPathFromPageId(substr($this->piVars['sections'],4)),1).'"':''); 00619 $what = '<div'.$this->pi_classParam('whatis').'><p>'.$what.'</p></div>'; 00620 $content = $what.$content; 00621 00622 // Return content: 00623 return $content; 00624 } 00625 00633 function compileResult($resultRows) { 00634 $content = ''; 00635 00636 // Transfer result rows to new variable, performing some mapping of sub-results etc. 00637 $newResultRows = array(); 00638 foreach($resultRows as $row) { 00639 $id = md5($row['phash_grouping']); 00640 if (is_array($newResultRows[$id])) { 00641 if (!$newResultRows[$id]['show_resume'] && $row['show_resume']) { // swapping: 00642 00643 // Remove old 00644 $subrows = $newResultRows[$id]['_sub']; 00645 unset($newResultRows[$id]['_sub']); 00646 $subrows[] = $newResultRows[$id]; 00647 00648 // Insert new: 00649 $newResultRows[$id] = $row; 00650 $newResultRows[$id]['_sub'] = $subrows; 00651 } else $newResultRows[$id]['_sub'][] = $row; 00652 } else { 00653 $newResultRows[$id] = $row; 00654 } 00655 } 00656 $resultRows = $newResultRows; 00657 00658 00659 switch($this->piVars['group']) { 00660 case 'sections': 00661 00662 $rl2flag = substr($this->piVars['sections'],0,2)=='rl'; 00663 $sections = array(); 00664 foreach($resultRows as $row) { 00665 $id = $row['rl0'].'-'.$row['rl1'].($rl2flag?'-'.$row['rl2']:''); 00666 $sections[$id][] = $row; 00667 } 00668 00669 $this->resultSections = array(); 00670 00671 foreach($sections as $id => $resultRows) { 00672 $rlParts = explode('-',$id); 00673 $theId = $rlParts[2] ? $rlParts[2] : ($rlParts[1]?$rlParts[1]:$rlParts[0]); 00674 $theRLid = $rlParts[2] ? 'rl2_'.$rlParts[2]:($rlParts[1]?'rl1_'.$rlParts[1]:'0'); 00675 00676 $sectionName = substr($this->getPathFromPageId($theId),1); 00677 if (!trim($sectionName)) { 00678 $sectionTitleLinked = $this->pi_getLL('unnamedSection','',1).':'; 00679 } else { 00680 $onclick = 'document.'.$this->prefixId.'[\''.$this->prefixId.'[_sections]\'].value=\''.$theRLid.'\';document.'.$this->prefixId.'.submit();return false;'; 00681 $sectionTitleLinked = '<a href="#" onclick="'.htmlspecialchars($onclick).'">'.htmlspecialchars($sectionName).':</a>'; 00682 } 00683 $this->resultSections[$id] = array($sectionName,count($resultRows)); 00684 00685 // Add content header: 00686 $content.= $this->makeSectionHeader($id,$sectionTitleLinked,count($resultRows)); 00687 00688 // Render result rows: 00689 foreach($resultRows as $row) { 00690 $content.= $this->printResultRow($row); 00691 } 00692 } 00693 break; 00694 default: // flat: 00695 foreach($resultRows as $row) { 00696 $content.= $this->printResultRow($row); 00697 } 00698 break; 00699 } 00700 return '<div'.$this->pi_classParam('res').'>'.$content.'</div>'; 00701 } 00702 00703 00704 00705 00706 00707 00708 00709 00710 00711 00712 00713 /*********************************** 00714 * 00715 * Searching functions (SQL) 00716 * 00717 ***********************************/ 00718 00726 function getPhashList($sWArr) { 00727 00728 // Initialize variables: 00729 $c=0; 00730 $totalHashList = array(); // This array accumulates the phash-values 00731 $this->wSelClauses = array(); 00732 00733 // Traverse searchwords; for each, select all phash integers and merge/diff/intersect them with previous word (based on operator) 00734 foreach($sWArr as $k => $v) { 00735 $GLOBALS['TT']->push('SearchWord '.$sWord); 00736 00737 // Making the query for a single search word based on the search-type 00738 $sWord = $v['sword']; // $GLOBALS['TSFE']->csConvObj->conv_case('utf-8',$v['sword'],'toLower'); // lower-case all of them... 00739 00740 $theType = (string)$this->piVars['type']; 00741 if (strstr($sWord,' ')) $theType = 20; // If there are spaces in the search-word, make a full text search instead. 00742 $res = ''; 00743 $wSel=''; 00744 00745 // Perform search for word: 00746 switch($theType) { 00747 case '1': 00748 $wSel = "IW.baseword LIKE '%".$GLOBALS['TYPO3_DB']->quoteStr($sWord, 'index_words')."%'"; 00749 $res = $this->execPHashListQuery($wSel,' AND is_stopword=0'); 00750 break; 00751 case '2': 00752 $wSel = "IW.baseword LIKE '".$GLOBALS['TYPO3_DB']->quoteStr($sWord, 'index_words')."%'"; 00753 $res = $this->execPHashListQuery($wSel,' AND is_stopword=0'); 00754 break; 00755 case '3': 00756 $wSel = "IW.baseword LIKE '%".$GLOBALS['TYPO3_DB']->quoteStr($sWord, 'index_words')."'"; 00757 $res = $this->execPHashListQuery($wSel,' AND is_stopword=0'); 00758 break; 00759 case '10': 00760 $wSel = 'IW.metaphone = '.$this->indexerObj->metaphone($sWord); 00761 $res = $this->execPHashListQuery($wSel,' AND is_stopword=0'); 00762 break; 00763 case '20': 00764 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery( 00765 'ISEC.phash', 00766 'index_section ISEC, index_fulltext IFT', 00767 'IFT.fulltextdata LIKE \'%'.$GLOBALS['TYPO3_DB']->quoteStr($sWord, 'index_fulltext').'%\' AND 00768 ISEC.phash = IFT.phash 00769 '.$this->sectionTableWhere(), 00770 'ISEC.phash' 00771 ); 00772 $wSel = '1=1'; 00773 00774 if ($this->piVars['type']==20) $this->piVars['order'] = 'mtime'; // If there is a fulltext search for a sentence there is a likeliness 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. 00775 break; 00776 default: 00777 $wSel = 'IW.wid = '.$hash = $this->indexerObj->md5inthash($sWord); 00778 $res = $this->execPHashListQuery($wSel,' AND is_stopword=0'); 00779 break; 00780 } 00781 00782 // Accumulate the word-select clauses 00783 $this->wSelClauses[] = $wSel; 00784 00785 // If there was a query to do, then select all phash-integers which resulted from this. 00786 if ($res) { 00787 00788 // Get phash list by searching for it: 00789 $phashList = array(); 00790 while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { 00791 $phashList[] = $row['phash']; 00792 } 00793 $GLOBALS['TYPO3_DB']->sql_free_result($res); 00794 00795 // Here the phash list are merged with the existing result based on whether we are dealing with OR, NOT or AND operations. 00796 if ($c) { 00797 switch($v['oper']) { 00798 case 'OR': 00799 $totalHashList = array_unique(array_merge($phashList,$totalHashList)); 00800 break; 00801 case 'AND NOT': 00802 $totalHashList = array_diff($totalHashList,$phashList); 00803 break; 00804 default: // AND... 00805 $totalHashList = array_intersect($totalHashList,$phashList); 00806 break; 00807 } 00808 } else { 00809 $totalHashList = $phashList; // First search 00810 } 00811 } 00812 00813 $GLOBALS['TT']->pull(); 00814 $c++; 00815 } 00816 00817 return implode(',',$totalHashList); 00818 } 00819 00827 function execPHashListQuery($wordSel,$plusQ='') { 00828 return $GLOBALS['TYPO3_DB']->exec_SELECTquery( 00829 'IR.phash', 00830 'index_words IW, 00831 index_rel IR, 00832 index_section ISEC', 00833 $wordSel.' 00834 AND IW.wid=IR.wid 00835 AND ISEC.phash = IR.phash 00836 '.$this->sectionTableWhere().' 00837 '.$plusQ, 00838 'IR.phash' 00839 ); 00840 } 00841 00847 function sectionTableWhere() { 00848 $out = $this->wholeSiteIdList<0 ? '' : 'AND ISEC.rl0 IN ('.$this->wholeSiteIdList.')'; 00849 00850 $match = ''; 00851 if (substr($this->piVars['sections'],0,4)=='rl1_') { 00852 $list = implode(',',t3lib_div::intExplode(',',substr($this->piVars['sections'],4))); 00853 $out.= 'AND ISEC.rl1 IN ('.$list.')'; 00854 $match = TRUE; 00855 } elseif (substr($this->piVars['sections'],0,4)=='rl2_') { 00856 $list = implode(',',t3lib_div::intExplode(',',substr($this->piVars['sections'],4))); 00857 $out.= 'AND ISEC.rl2 IN ('.$list.')'; 00858 $match = TRUE; 00859 } elseif (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['addRootLineFields'])) { 00860 // Traversing user configured fields to see if any of those are used to limit search to a section: 00861 foreach($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['addRootLineFields'] as $fieldName => $rootLineLevel) { 00862 if (substr($this->piVars['sections'],0,strlen($fieldName)+1)==$fieldName.'_') { 00863 $list = implode(',',t3lib_div::intExplode(',',substr($this->piVars['sections'],strlen($fieldName)+1))); 00864 $out.= 'AND ISEC.'.$fieldName.' IN ('.$list.')'; 00865 $match = TRUE; 00866 break; 00867 } 00868 } 00869 } 00870 00871 // If no match above, test the static types: 00872 if (!$match) { 00873 switch((string)$this->piVars['sections']) { 00874 case '-1': // '-1' => 'Only this page', 00875 $out.= ' AND ISEC.page_id='.$GLOBALS['TSFE']->id; 00876 break; 00877 case '-2': // '-2' => 'Top + level 1', 00878 $out.= ' AND ISEC.rl2=0'; 00879 break; 00880 case '-3': // '-3' => 'Level 2 and out', 00881 $out.= ' AND ISEC.rl2>0'; 00882 break; 00883 } 00884 } 00885 00886 return $out; 00887 } 00888 00894 function mediaTypeWhere() { 00895 00896 switch((string)$this->piVars['media']) { 00897 case '0': // '0' => 'Kun TYPO3 sider', 00898 $out = 'AND IP.item_type='.$GLOBALS['TYPO3_DB']->fullQuoteStr('0', 'index_phash');; 00899 break; 00900 case '-2': // All external documents 00901 $out = 'AND IP.item_type!='.$GLOBALS['TYPO3_DB']->fullQuoteStr('0', 'index_phash');; 00902 break; 00903 case '-1': // All content 00904 $out=''; 00905 break; 00906 default: 00907 $out = 'AND IP.item_type='.$GLOBALS['TYPO3_DB']->fullQuoteStr($this->piVars['media'], 'index_phash'); 00908 break; 00909 } 00910 00911 return $out; 00912 } 00913 00919 function languageWhere() { 00920 if ($this->piVars['lang']>=0) { // -1 is the same as ALL language. 00921 return 'AND IP.sys_language_uid='.intval($this->piVars['lang']); 00922 } 00923 } 00924 00931 function execFinalQuery($list) { 00932 00933 // Setting up methods of filtering results based on page types, access, etc. 00934 $page_join = ''; 00935 $page_where = ''; 00936 00937 // Calling hook for alternative creation of page ID list 00938 if ($hookObj = &$this->hookRequest('execFinalQuery_idList')) { 00939 $page_where = $hookObj->execFinalQuery_idList($list); 00940 } elseif ($this->join_pages) { // Alternative to getting all page ids by ->getTreeList() where "excludeSubpages" is NOT respected. 00941 $page_join = ', 00942 pages'; 00943 $page_where = 'pages.uid = ISEC.page_id 00944 '.$this->cObj->enableFields('pages').' 00945 AND pages.no_search=0 00946 AND pages.doktype<200 00947 '; 00948 } elseif ($this->wholeSiteIdList>=0) { // Collecting all pages IDs in which to search; filtering out ALL pages that are not accessible due to enableFields. Does NOT look for "no_search" field! 00949 $siteIdNumbers = t3lib_div::intExplode(',',$this->wholeSiteIdList); 00950 $id_list=array(); 00951 while(list(,$rootId)=each($siteIdNumbers)) { 00952 $id_list[] = $this->cObj->getTreeList($rootId,9999,0,0,'','').$rootId; 00953 } 00954 $page_where = 'ISEC.page_id IN ('.implode(',',$id_list).')'; 00955 } else { // Disable everything... (select all) 00956 $page_where = ' 1=1 '; 00957 } 00958 00959 00960 // 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. 00961 if (substr($this->piVars['order'],0,5)=='rank_') { 00962 /* 00963 OK there were some fancy calculations promoted by Graeme Merrall: 00964 00965 "However, regarding relevance you probably want to look at something like 00966 Salton's formula which is a good easy way to measure relevance. 00967 Oracle Intermedia uses this and it's pretty simple: 00968 Score can be between 0 and 100, but the top-scoring document in the query 00969 will not necessarily have a score of 100 -- scoring is relative, not 00970 absolute. This means that scores are not comparable across indexes, or even 00971 across different queries on the same index. Score for each document is 00972 computed using the standard Salton formula: 00973 00974 3f(1+log(N/n)) 00975 00976 Where f is the frequency of the search term in the document, N is the total 00977 number of rows in the table, and n is the number of rows which contain the 00978 search term. This is converted into an integer in the range 0 - 100. 00979 00980 There's a good doc on it at 00981 http://ls6-www.informatik.uni-dortmund.de/bib/fulltext/ir/Pfeifer:97/ 00982 although it may be a little complex for what you require so just pick the 00983 relevant parts out. 00984 " 00985 00986 However I chose not to go with this for several reasons. 00987 I do not claim that my ways of calculating importance here is the best. 00988 ANY (better) suggestion for ranking calculation is accepted! (as long as they are shipped with tested code in exchange for this.) 00989 */ 00990 00991 switch($this->piVars['order']) { 00992 case 'rank_flag': // This gives priority to word-position (max-value) so that words in title, keywords, description counts more than in content. 00993 // The ordering is refined with the frequency sum as well. 00994 $grsel = 'MAX(IR.flags) AS order_val1, SUM(IR.freq) AS order_val2'; 00995 $orderBy = 'order_val1'.$this->isDescending().',order_val2'.$this->isDescending(); 00996 break; 00997 case 'rank_first': // Results in average position of search words on page. Must be inversely sorted (low numbers are closer to top) 00998 $grsel = 'AVG(IR.first) AS order_val'; 00999 $orderBy = 'order_val'.$this->isDescending(1); 01000 break; 01001 case 'rank_count': // Number of words found 01002 $grsel = 'SUM(IR.count) AS order_val'; 01003 $orderBy = 'order_val'.$this->isDescending(); 01004 break; 01005 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? 01006 $grsel = 'SUM(IR.freq) AS order_val'; 01007 $orderBy = 'order_val'.$this->isDescending(); 01008 break; 01009 } 01010 01011 // So, words are imploded into an OR statement (no "sentence search" should be done here - may deselect results) 01012 $wordSel='('.implode(' OR ',$this->wSelClauses).') AND '; 01013 01014 return $GLOBALS['TYPO3_DB']->exec_SELECTquery( 01015 'ISEC.*, IP.*, ' 01016 .$grsel, 01017 'index_words IW, 01018 index_rel IR, 01019 index_section ISEC, 01020 index_phash IP'. 01021 $page_join, 01022 $wordSel.' 01023 IP.phash IN ('.$list.') '. 01024 $this->mediaTypeWhere().' '. 01025 $this->languageWhere().' 01026 AND IW.wid=IR.wid 01027 AND ISEC.phash = IR.phash 01028 AND IP.phash = IR.phash 01029 AND '.$page_where, 01030 'IP.phash,ISEC.phash,ISEC.phash_t3,ISEC.rl0,ISEC.rl1,ISEC.rl2 ,ISEC.page_id,ISEC.uniqid,IP.phash_grouping,IP.data_filename ,IP.data_page_id ,IP.data_page_reg1,IP.data_page_type,IP.data_page_mp,IP.gr_list,IP.item_type,IP.item_title,IP.item_description,IP.item_mtime,IP.tstamp,IP.item_size,IP.contentHash,IP.crdate,IP.parsetime,IP.sys_language_uid,IP.item_crdate,IP.cHashParams,IP.externalUrl,IP.recordUid,IP.freeIndexUid', 01031 $orderBy 01032 ); 01033 } else { // Otherwise, if sorting are done with the pages table or other fields, there is no need for joining with the rel/word tables: 01034 01035 $orderBy = ''; 01036 switch((string)$this->piVars['order']) { 01037 case 'title': 01038 $orderBy = 'IP.item_title'.$this->isDescending(); 01039 break; 01040 case 'crdate': 01041 $orderBy = 'IP.item_crdate'.$this->isDescending(); 01042 break; 01043 case 'mtime': 01044 $orderBy = 'IP.item_mtime'.$this->isDescending(); 01045 break; 01046 } 01047 01048 return $GLOBALS['TYPO3_DB']->exec_SELECTquery( 01049 'ISEC.*, IP.*', 01050 'index_phash IP,index_section ISEC'.$page_join, 01051 'IP.phash IN ('.$list.') '. 01052 $this->mediaTypeWhere().' '. 01053 $this->languageWhere().' 01054 AND IP.phash = ISEC.phash 01055 AND '.$page_where, 01056 'IP.phash,ISEC.phash,ISEC.phash_t3,ISEC.rl0,ISEC.rl1,ISEC.rl2 ,ISEC.page_id,ISEC.uniqid,IP.phash_grouping,IP.data_filename ,IP.data_page_id ,IP.data_page_reg1,IP.data_page_type,IP.data_page_mp,IP.gr_list,IP.item_type,IP.item_title,IP.item_description,IP.item_mtime,IP.tstamp,IP.item_size,IP.contentHash,IP.crdate,IP.parsetime,IP.sys_language_uid,IP.item_crdate,IP.cHashParams,IP.externalUrl,IP.recordUid,IP.freeIndexUid', 01057 $orderBy 01058 ); 01059 } 01060 } 01061 01069 function checkResume($row) { 01070 01071 // If the record is indexed by an indexing configuration, just show it. 01072 // At least this is needed for external URLs and files. 01073 // For records we might need to extend this - for instance block display if record is access restricted. 01074 if ($row['freeIndexUid']) { 01075 return TRUE; 01076 } 01077 01078 // Evaluate regularly indexed pages based on item_type: 01079 if ($row['item_type']) { // External media: 01080 // For external media we will check the access of the parent page on which the media was linked from. 01081 // "phash_t3" is the phash of the parent TYPO3 page row which initiated the indexing of the documents in this section. 01082 // So, selecting for the grlist records belonging to the parent phash-row where the current users gr_list exists will help us to know. 01083 // 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 better safe than sorry. 01084 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('phash', 'index_grlist', 'phash='.intval($row['phash_t3']).' AND gr_list='.$GLOBALS['TYPO3_DB']->fullQuoteStr($GLOBALS['TSFE']->gr_list, 'index_grlist')); 01085 if ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) { 01086 #debug("Look up for external media '".$row['data_filename']."': phash:".$row['phash_t3'].' YES - ('.$GLOBALS['TSFE']->gr_list.")!",1); 01087 return TRUE; 01088 } else { 01089 #debug("Look up for external media '".$row['data_filename']."': phash:".$row['phash_t3'].' NO - ('.$GLOBALS['TSFE']->gr_list.")!",1); 01090 return FALSE; 01091 } 01092 } else { // Ordinary TYPO3 pages: 01093 if (strcmp($row['gr_list'],$GLOBALS['TSFE']->gr_list)) { 01094 // 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... 01095 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('phash', 'index_grlist', 'phash='.intval($row['phash']).' AND gr_list='.$GLOBALS['TYPO3_DB']->fullQuoteStr($GLOBALS['TSFE']->gr_list, 'index_grlist')); 01096 if ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) { 01097 #debug('Checking on it ...'.$row['item_title'].'/'.$row['phash'].' - YES ('.$GLOBALS['TSFE']->gr_list.")",1); 01098 return TRUE; 01099 } else { 01100 #debug('Checking on it ...'.$row['item_title'].'/'.$row['phash']." - NOPE",1); 01101 return FALSE; 01102 } 01103 } else { 01104 #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); 01105 return TRUE; 01106 } 01107 } 01108 } 01109 01116 function isDescending($inverse=FALSE) { 01117 $desc = $this->piVars['desc']; 01118 if ($inverse) $desc=!$desc; 01119 return !$desc ? ' DESC':''; 01120 } 01121 01130 function writeSearchStat($sWArr,$count,$pt) { 01131 $insertFields = array( 01132 'searchstring' => $this->piVars['sword'], 01133 'searchoptions' => serialize(array($this->piVars,$sWArr,$pt)), 01134 'feuser_id' => intval($this->fe_user->user['uid']), // fe_user id, integer 01135 'cookie' => $this->fe_user->id, // cookie as set or retrieve. If people has cookies disabled this will vary all the time... 01136 'IP' => t3lib_div::getIndpEnv('REMOTE_ADDR'), // Remote IP address 01137 'hits' => intval($count), // Number of hits on the search. 01138 'tstamp' => $GLOBALS['EXEC_TIME'] // Time stamp 01139 ); 01140 01141 $GLOBALS['TYPO3_DB']->exec_INSERTquery('index_stat_search', $insertFields); 01142 $newId = $GLOBALS['TYPO3_DB']->sql_insert_id(); 01143 01144 if ($newId) { 01145 foreach($sWArr as $val) { 01146 $insertFields = array( 01147 'word' => $val['sword'], // $GLOBALS['TSFE']->csConvObj->conv_case('utf-8', $val['sword'], 'toLower'), 01148 'index_stat_search_id' => $newId, 01149 'tstamp' => $GLOBALS['EXEC_TIME'], // Time stamp 01150 'pageid' => $GLOBALS['TSFE']->id //search page id for indexed search stats 01151 ); 01152 01153 $GLOBALS['TYPO3_DB']->exec_INSERTquery('index_stat_word', $insertFields); 01154 } 01155 } 01156 } 01157 01158 01159 01160 01161 01162 01163 01164 01165 01166 01167 01168 01169 01170 /*********************************** 01171 * 01172 * HTML output functions 01173 * 01174 ***********************************/ 01175 01182 function makeSearchForm($optValues) { 01183 01184 // Accumulate table rows here: 01185 $rows = array(); 01186 01187 // Adding search field and button: 01188 $rows[]='<tr> 01189 <td nowrap="nowrap"><p>'.$this->pi_getLL('form_searchFor','',1).' </p></td> 01190 <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','',1).'"'.$this->pi_classParam('searchbox-button').' /></td> 01191 </tr>'; 01192 01193 if ($this->conf['show.']['clearSearchBox'] && $this->conf['show.']['clearSearchBox.']['enableSubSearchCheckBox']) { 01194 $rows[]='<tr> 01195 <td></td> 01196 <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"':'').' /> '.$this->pi_getLL('makerating_addToCurrentSearch').'</td> 01197 </tr>'; 01198 } 01199 01200 // Extended search options: 01201 if ($this->piVars['ext']) { 01202 if (is_array($optValues['type']) || is_array($optValues['defOp'])) $rows[]='<tr> 01203 <td nowrap="nowrap"><p>'.$this->pi_getLL('form_match','',1).' </p></td> 01204 <td>'.$this->renderSelectBox($this->prefixId.'[type]',$this->piVars['type'],$optValues['type']). 01205 $this->renderSelectBox($this->prefixId.'[defOp]',$this->piVars['defOp'],$optValues['defOp']).'</td> 01206 </tr>'; 01207 if (is_array($optValues['media']) || is_array($optValues['lang'])) $rows[]='<tr> 01208 <td nowrap="nowrap"><p>'.$this->pi_getLL('form_searchIn','',1).' </p></td> 01209 <td>'.$this->renderSelectBox($this->prefixId.'[media]',$this->piVars['media'],$optValues['media']). 01210 $this->renderSelectBox($this->prefixId.'[lang]',$this->piVars['lang'],$optValues['lang']).'</td> 01211 </tr>'; 01212 if (is_array($optValues['sections'])) $rows[]='<tr> 01213 <td nowrap="nowrap"><p>'.$this->pi_getLL('form_fromSection','',1).' </p></td> 01214 <td>'.$this->renderSelectBox($this->prefixId.'[sections]',$this->piVars['sections'],$optValues['sections']).'</td> 01215 </tr>'; 01216 if (is_array($optValues['order']) || is_array($optValues['desc']) || is_array($optValues['results'])) $rows[]='<tr> 01217 <td nowrap="nowrap"><p>'.$this->pi_getLL('form_orderBy','',1).' </p></td> 01218 <td><p>'.$this->renderSelectBox($this->prefixId.'[order]',$this->piVars['order'],$optValues['order']). 01219 $this->renderSelectBox($this->prefixId.'[desc]',$this->piVars['desc'],$optValues['desc']). 01220 $this->renderSelectBox($this->prefixId.'[results]',$this->piVars['results'],$optValues['results']).' '.$this->pi_getLL('form_atATime','',1).'</p></td> 01221 </tr>'; 01222 if (is_array($optValues['group']) || !$this->conf['blind.']['extResume']) $rows[]='<tr> 01223 <td nowrap="nowrap"><p>'.$this->pi_getLL('form_style','',1).' </p></td> 01224 <td><p>'.$this->renderSelectBox($this->prefixId.'[group]',$this->piVars['group'],$optValues['group']). 01225 (!$this->conf['blind.']['extResume'] ? ' 01226 <input type="hidden" name="'.$this->prefixId.'[extResume]" value="0" /><input type="checkbox" value="1" name="'.$this->prefixId.'[extResume]"'.($this->piVars['extResume']?' checked="checked"':'').' />'.$this->pi_getLL('form_extResume','',1):'').'</p></td> 01227 </tr>'; 01228 } 01229 01230 // Compile rows into a table, wrapped in form-tags: 01231 $out=' 01232 <form action="'.htmlspecialchars($this->pi_getPageLink($GLOBALS['TSFE']->id,$GLOBALS['TSFE']->sPre)).'" method="post" name="'.$this->prefixId.'" style="margin: 0 0 0 0;"> 01233 <table '.$this->conf['tableParams.']['searchBox'].'> 01234 '.implode(chr(10),$rows).' 01235 </table> 01236 <input type="hidden" name="'.$this->prefixId.'[_sections]" value="0" /> 01237 <input type="hidden" name="'.$this->prefixId.'[pointer]" value="0" /> 01238 <input type="hidden" name="'.$this->prefixId.'[ext]" value="'.($this->piVars['ext']?1:0).'" /> 01239 </form>'; 01240 $out.='<p>'. 01241 ($this->piVars['ext'] ? 01242 '<a href="'.htmlspecialchars($this->pi_getPageLink($GLOBALS['TSFE']->id,$GLOBALS['TSFE']->sPre,array($this->prefixId.'[ext]'=>0))).'">'.$this->pi_getLL('link_regularSearch','',1).'</a>' : 01243 '<a href="'.htmlspecialchars($this->pi_getPageLink($GLOBALS['TSFE']->id,$GLOBALS['TSFE']->sPre,array($this->prefixId.'[ext]'=>1))).'">'.$this->pi_getLL('link_advancedSearch','',1).'</a>' 01244 ).'</p>'; 01245 01246 return '<div'.$this->pi_classParam('searchbox').'>'.$out.'</div>'; 01247 } 01248 01254 function printRules() { 01255 $out = ''; 01256 if ($this->conf['show.']['rules']) { 01257 $out = '<h2>'.$this->pi_getLL('rules_header','',1).'</h2> 01258 <p>'.nl2br(trim($this->pi_getLL('rules_text','',1))).'</p>'; 01259 $out = ' 01260 <div'.$this->pi_classParam('rules').'>'.$this->cObj->stdWrap($out, $this->conf['rules_stdWrap.']).'</div>'; 01261 } 01262 return $out; 01263 } 01264 01270 function printResultSectionLinks() { 01271 if (count($this->resultSections)) { 01272 $lines = array(); 01273 01274 foreach($this->resultSections as $id => $dat) { 01275 $lines[] = '<li><a href="'.htmlspecialchars($GLOBALS['TSFE']->anchorPrefix.'#'.md5($id)).'">'. 01276 htmlspecialchars(trim($dat[0])?trim($dat[0]):$this->pi_getLL('unnamedSection')).' ('.$dat[1].' '.$this->pi_getLL($dat[1]>1?'word_pages':'word_page','',1).')'. 01277 '</a></li>'; 01278 } 01279 $out = '<ul>'.implode(chr(10),$lines).'</ul>'; 01280 return '<div'.$this->pi_classParam('sectionlinks').'>'.$this->cObj->stdWrap($out, $this->conf['sectionlinks_stdWrap.']).'</div>'; 01281 } 01282 } 01283 01292 function makeSectionHeader($id,$sectionTitleLinked,$countResultRows) { 01293 return '<div'.$this->pi_classParam('secHead').'><a name="'.md5($id).'"></a><table '.$this->conf['tableParams.']['secHead'].'> 01294 <tr> 01295 <td width="95%"><h2>'.$sectionTitleLinked.'</h2></td> 01296 <td align="right" nowrap="nowrap"><p>'.$countResultRows.' '.$this->pi_getLL($countResultRows>1?'word_pages':'word_page','',1).'</p></td> 01297 </tr> 01298 </table></div>'; 01299 } 01300 01308 function printResultRow($row, $headerOnly=0) { 01309 01310 // Get template content: 01311 $tmplContent = $this->prepareResultRowTemplateData($row, $headerOnly); 01312 01313 if ($hookObj = &$this->hookRequest('printResultRow')) { 01314 return $hookObj->printResultRow($row, $headerOnly, $tmplContent); 01315 } else { 01316 01317 // Make the header row with title, icon and rating bar.: 01318 $out.='<tr '.$this->pi_classParam('title'.$tmplContent['CSSsuffix']).'> 01319 <td width="16" '.$this->pi_classParam('title-icon'.$tmplContent['CSSsuffix']).'>'.$tmplContent['icon'].'</td> 01320 <td width="95%" nowrap="nowrap"><p>'. 01321 #$row['phash'].' // '. 01322 '<span '.$this->pi_classParam('title-number'.$tmplContent['CSSsuffix']).'>'.$tmplContent['result_number'].': </span>'. 01323 '<span '.$this->pi_classParam('title-caption'.$tmplContent['CSSsuffix']).'>'.$tmplContent['title'].'</span>'. 01324 '</p></td> 01325 <td nowrap="nowrap"><p'.$this->pi_classParam('percent'.$tmplContent['CSSsuffix']).'>'.$tmplContent['rating'].'</p></td> 01326 </tr>'; 01327 01328 // Print the resume-section. If headerOnly is 1, then only the short resume is printed 01329 if (!$headerOnly) { 01330 $out.='<tr> 01331 <td></td> 01332 <td colspan="2"'.$this->pi_classParam('descr'.$tmplContent['CSSsuffix']).'><p>'.$tmplContent['description'].'</p></td> 01333 </tr>'; 01334 $out.='<tr> 01335 <td></td> 01336 <td '.$this->pi_classParam('info'.$tmplContent['CSSsuffix']).' nowrap="nowrap"><p>'. 01337 $tmplContent['size'].' - '.$tmplContent['created'].' - '.$tmplContent['modified']. 01338 ($tmplContent['path'] ? '<br/>'.$this->pi_getLL('res_path','',1).' '.$tmplContent['path'] : ''). 01339 '</p></td> 01340 <td '.$this->pi_classParam('info'.$tmplContent['CSSsuffix']).' align="right"><p>'.$tmplContent['access'].$tmplContent['language'].'</p></td> 01341 </tr>'; 01342 } elseif ($headerOnly==1) { 01343 $out.='<tr> 01344 <td></td> 01345 <td colspan="2"'.$this->pi_classParam('descr'.$tmplContent['CSSsuffix']).'><p>'.$tmplContent['description'].'</p></td> 01346 </tr>'; 01347 } 01348 01349 // If there are subrows (eg. subpages in a PDF-file or if a duplicate page is selected due to user-login (phash_grouping)) 01350 if (is_array($row['_sub'])) { 01351 if ($this->multiplePagesType($row['item_type'])) { 01352 $out.='<tr> 01353 <td></td> 01354 <td colspan="2"><p><br/>'.$this->pi_getLL('res_otherMatching','',1).'<br/><br/></p></td> 01355 </tr>'; 01356 01357 foreach($row['_sub'] as $subRow) { 01358 $out.='<tr> 01359 <td></td> 01360 <td colspan="2"><p>'.$this->printResultRow($subRow,1).'</p></td> 01361 </tr>'; 01362 } 01363 } else { 01364 $out.='<tr> 01365 <td></td> 01366 <td colspan="2"><p>'.$this->pi_getLL('res_otherPageAsWell','',1).'</p></td> 01367 </tr>'; 01368 } 01369 } 01370 01371 return '<table '.$this->conf['tableParams.']['searchRes'].'>'.$out.'</table><br/>'; 01372 } 01373 } 01374 01383 function pi_list_browseresults($showResultCount=1,$addString='',$addPart='') { 01384 01385 // Initializing variables: 01386 $pointer=$this->piVars['pointer']; 01387 $count=$this->internal['res_count']; 01388 $results_at_a_time = t3lib_div::intInRange($this->internal['results_at_a_time'],1,1000); 01389 $maxPages = t3lib_div::intInRange($this->internal['maxPages'],1,100); 01390 $max = t3lib_div::intInRange(ceil($count/$results_at_a_time),1,$maxPages); 01391 $pointer=intval($pointer); 01392 $links=array(); 01393 01394 // Make browse-table/links: 01395 if ($pointer>0) { 01396 $links[]='<td><p>'.$this->makePointerSelector_link($this->pi_getLL('pi_list_browseresults_prev','< Previous',1),$pointer-1).'</p></td>'; 01397 } 01398 for($a=0;$a<$max;$a++) { 01399 $links[]='<td'.($pointer==$a?$this->pi_classParam('browsebox-SCell'):'').'><p>'.$this->makePointerSelector_link(trim($this->pi_getLL('pi_list_browseresults_page','Page',1).' '.($a+1)),$a).'</p></td>'; 01400 } 01401 if ($pointer<ceil($count/$results_at_a_time)-1) { 01402 $links[]='<td><p>'.$this->makePointerSelector_link($this->pi_getLL('pi_list_browseresults_next','Next >',1),$pointer+1).'</p></td>'; 01403 } 01404 01405 $pR1 = $pointer*$results_at_a_time+1; 01406 $pR2 = $pointer*$results_at_a_time+$results_at_a_time; 01407 $sTables = '<div'.$this->pi_classParam('browsebox').'>'. 01408 ($showResultCount ? '<p>'.sprintf( 01409 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>')), 01410 $pR1, 01411 min(array($this->internal['res_count'],$pR2)), 01412 $this->internal['res_count'] 01413 ).$addString.'</p>':'' 01414 ).$addPart. 01415 '<table> 01416 <tr>'.implode('',$links).'</tr> 01417 </table></div>'; 01418 01419 return $sTables; 01420 } 01421 01422 01423 01424 01425 01426 01427 01428 01429 01430 01431 01432 01433 /*********************************** 01434 * 01435 * Support functions for HTML output (with a minimum of fixed markup) 01436 * 01437 ***********************************/ 01438 01446 function prepareResultRowTemplateData($row, $headerOnly) { 01447 01448 // Initialize: 01449 $specRowConf = $this->getSpecialConfigForRow($row); 01450 $CSSsuffix = $specRowConf['CSSsuffix']?'-'.$specRowConf['CSSsuffix']:''; 01451 01452 // If external media, link to the media-file instead. 01453 if ($row['item_type']) { // External media 01454 if ($row['show_resume']) { // Can link directly. 01455 $title = '<a href="'.htmlspecialchars($row['data_filename']).'">'.htmlspecialchars($this->makeTitle($row)).'</a>'; 01456 } else { // Suspicious, so linking to page instead... 01457 $copy_row = $row; 01458 unset($copy_row['cHashParams']); 01459 $title = $this->linkPage($row['page_id'],htmlspecialchars($this->makeTitle($row)),$copy_row); 01460 } 01461 } else { // Else the page: 01462 01463 // Prepare search words for markup in content: 01464 if ($this->conf['forwardSearchWordsInResultLink']) { 01465 $markUpSwParams = array('no_cache' => 1); 01466 foreach($this->sWArr as $d) { 01467 $markUpSwParams['sword_list'][] = $d['sword']; 01468 } 01469 } else { 01470 $markUpSwParams = array(); 01471 } 01472 $title = $this->linkPage($row['data_page_id'],htmlspecialchars($this->makeTitle($row)),$row,$markUpSwParams); 01473 } 01474 01475 $tmplContent = array(); 01476 $tmplContent['title'] = $title; 01477 $tmplContent['result_number'] = $row['result_number']; 01478 $tmplContent['icon'] = $this->makeItemTypeIcon($row['item_type'],'',$specRowConf); 01479 $tmplContent['rating'] = $this->makeRating($row); 01480 $tmplContent['description'] = $this->makeDescription($row,$this->piVars['extResume'] && !$headerOnly?0:1); 01481 $tmplContent = $this->makeInfo($row,$tmplContent); 01482 $tmplContent['access'] = $this->makeAccessIndication($row['page_id']); 01483 $tmplContent['language'] = $this->makeLanguageIndication($row); 01484 $tmplContent['CSSsuffix'] = $CSSsuffix; 01485 01486 // Post processing with hook. 01487 if ($hookObj = &$this->hookRequest('prepareResultRowTemplateData_postProc')) { 01488 $tmplContent = $hookObj->prepareResultRowTemplateData_postProc($tmplContent, $row, $headerOnly); 01489 } 01490 01491 return $tmplContent; 01492 } 01493 01500 function tellUsWhatIsSeachedFor($sWArr) { 01501 01502 // Init: 01503 $searchingFor = ''; 01504 $c=0; 01505 01506 // Traverse search words: 01507 foreach($sWArr as $k => $v) { 01508 if ($c) { 01509 switch($v['oper']) { 01510 case 'OR': 01511 $searchingFor.= ' '.$this->pi_getLL('searchFor_or','',1).' '.$this->wrapSW($this->utf8_to_currentCharset($v['sword'])); 01512 break; 01513 case 'AND NOT': 01514 $searchingFor.= ' '.$this->pi_getLL('searchFor_butNot','',1).' '.$this->wrapSW($this->utf8_to_currentCharset($v['sword'])); 01515 break; 01516 default: // AND... 01517 $searchingFor.= ' '.$this->pi_getLL('searchFor_and','',1).' '.$this->wrapSW($this->utf8_to_currentCharset($v['sword'])); 01518 break; 01519 } 01520 } else { 01521 $searchingFor = $this->pi_getLL('searchFor','',1).' '.$this->wrapSW($this->utf8_to_currentCharset($v['sword'])); 01522 } 01523 $c++; 01524 } 01525 return $searchingFor; 01526 } 01527 01534 function wrapSW($str) { 01535 return '"<span'.$this->pi_classParam('sw').'>'.htmlspecialchars($str).'</span>"'; 01536 } 01537 01546 function renderSelectBox($name,$value,$optValues) { 01547 if (is_array($optValues)) { 01548 $opt = array(); 01549 $isSelFlag = 0; 01550 01551 foreach($optValues as $k => $v) { 01552 $sel = (!strcmp($k,$value) ? ' selected="selected"' : ''); 01553 if ($sel) $isSelFlag++; 01554 $opt[] = '<option value="'.htmlspecialchars($k).'"'.$sel.'>'.htmlspecialchars($v).'</option>'; 01555 } 01556 01557 return '<select name="'.$name.'">'.implode('',$opt).'</select>'; 01558 } 01559 } 01560 01569 function makePointerSelector_link($str,$p) { 01570 $onclick = 'document.'.$this->prefixId.'[\''.$this->prefixId.'[pointer]\'].value=\''.$p.'\';document.'.$this->prefixId.'.submit();return false;'; 01571 return '<a href="#" onclick="'.htmlspecialchars($onclick).'">'.$str.'</a>'; 01572 } 01573 01582 function makeItemTypeIcon($it,$alt='',$specRowConf) { 01583 if (!isset($this->iconFileNameCache[$it])) { 01584 $this->iconFileNameCache[$it] = ''; 01585 01586 // If TypoScript is used to render the icon: 01587 if (is_array($this->conf['iconRendering.'])) { 01588 $this->cObj->setCurrentVal($it); 01589 $this->iconFileNameCache[$it] = $this->cObj->cObjGetSingle($this->conf['iconRendering'],$this->conf['iconRendering.']); 01590 } else { // ... otherwise, get flag from sys_language record: 01591 01592 // Default creation / finding of icon: 01593 $icon = ''; 01594 if ($it==='0') { 01595 if (is_array($specRowConf['pageIcon.'])) { 01596 $this->iconFileNameCache[$it] = $this->cObj->IMAGE($specRowConf['pageIcon.']); 01597 } else { 01598 $icon = 'EXT:indexed_search/pi/res/pages.gif'; 01599 } 01600 } elseif ($this->external_parsers[$it]) { 01601 $icon = $this->external_parsers[$it]->getIcon($it); 01602 } 01603 01604 if ($icon) { 01605 $fullPath = t3lib_div::getFileAbsFileName($icon); 01606 01607 if ($fullPath) { 01608 $info = @getimagesize($fullPath); 01609 $iconPath = substr($fullPath,strlen(PATH_site)); 01610 $this->iconFileNameCache[$it] = is_array($info) ? '<img src="'.$iconPath.'" '.$info[3].' title="'.htmlspecialchars($alt).'" alt="" />' : ''; 01611 } 01612 } 01613 } 01614 } 01615 return $this->iconFileNameCache[$it]; 01616 } 01617 01624 function makeRating($row) { 01625 01626 switch((string)$this->piVars['order']) { 01627 case 'rank_count': // Number of occurencies on page 01628 return $row['order_val'].' '.$this->pi_getLL('maketitle_matches'); 01629 break; 01630 case 'rank_first': // Close to top of page 01631 return ceil(t3lib_div::intInRange(255-$row['order_val'],1,255)/255*100).'%'; 01632 break; 01633 case 'rank_flag': // Based on priority assigned to <title> / <meta-keywords> / <meta-description> / <body> 01634 if ($this->firstRow['order_val2']) { 01635 $base = $row['order_val1']*256; // (3 MSB bit, 224 is highest value of order_val1 currently) 01636 $freqNumber = $row['order_val2']/$this->firstRow['order_val2']*pow(2,12); // 15-3 MSB = 12 01637 $total = t3lib_div::intInRange($base+$freqNumber,0,32767); 01638 #debug($total); 01639 return ceil(log($total)/log(32767)*100).'%'; 01640 } 01641 break; 01642 case 'rank_freq': // Based on frequency 01643 $max = 10000; 01644 $total = t3lib_div::intInRange($row['order_val'],0,$max); 01645 # debug($total); 01646 return ceil(log($total)/log($max)*100).'%'; 01647 break; 01648 case 'crdate': // Based on creation date 01649 return $this->cObj->calcAge(time()-$row['item_crdate'],0); // ,$conf['age'] 01650 break; 01651 case 'mtime': // Based on modification time 01652 return $this->cObj->calcAge(time()-$row['item_mtime'],0); // ,$conf['age'] 01653 break; 01654 default: // fx. title 01655 return ' '; 01656 break; 01657 } 01658 } 01659 01668 function makeDescription($row,$noMarkup=0,$lgd=180) { 01669 if ($row['show_resume']) { 01670 if (!$noMarkup) { 01671 $markedSW = ''; 01672 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', 'index_fulltext', 'phash='.intval($row['phash'])); 01673 if ($ftdrow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { 01674 $markedSW = $this->markupSWpartsOfString($ftdrow['fulltextdata']); 01675 } 01676 $GLOBALS['TYPO3_DB']->sql_free_result($res); 01677 } 01678 01679 if (trim($markedSW)) { 01680 return $this->utf8_to_currentCharset($markedSW); 01681 } else { 01682 $outputStr = $GLOBALS['TSFE']->csConvObj->crop('utf-8',$row['item_description'],$lgd); 01683 $outputStr = htmlspecialchars($outputStr); 01684 01685 return $this->utf8_to_currentCharset($outputStr); 01686 } 01687 } else { 01688 return '<span class="noResume">'.$this->pi_getLL('res_noResume','',1).'</span>'; 01689 } 01690 } 01691 01698 function markupSWpartsOfString($str) { 01699 01700 // Init: 01701 $str = str_replace(' ',' ',t3lib_parsehtml::bidir_htmlspecialchars($str,-1)); 01702 $str = ereg_replace('[[:space:]]+',' ',$str); 01703 $swForReg = array(); 01704 01705 // Prepare search words for regex: 01706 foreach($this->sWArr as $d) { 01707 $swForReg[] = quotemeta($d['sword']); 01708 } 01709 $regExString = '('.implode('|',$swForReg).')'; 01710 01711 // Split and combine: 01712 $parts = preg_split('/'.$regExString.'/i', ' '.$str.' ', 20000, PREG_SPLIT_DELIM_CAPTURE); 01713 #debug($parts,$regExString); 01714 // Constants: 01715 $summaryMax = 300; 01716 $postPreLgd = 60; 01717 $postPreLgd_offset = 5; 01718 $divider = ' ... '; 01719 01720 $occurencies = (count($parts)-1)/2; 01721 if ($occurencies) { 01722 $postPreLgd = t3lib_div::intInRange($summaryMax/$occurencies,$postPreLgd,$summaryMax/2); 01723 } 01724 01725 // Variable: 01726 $summaryLgd = 0; 01727 $output = array(); 01728 01729 // Shorten in-between strings: 01730 foreach($parts as $k => $strP) { 01731 if (($k%2)==0) { 01732 01733 // Find length of the summary part: 01734 $strLen = $GLOBALS['TSFE']->csConvObj->strlen('utf-8', $parts[$k]); 01735 $output[$k] = $parts[$k]; 01736 01737 // Possibly shorten string: 01738 if (!$k) { // First entry at all (only cropped on the frontside) 01739 if ($strLen > $postPreLgd) { 01740 $output[$k] = $divider.ereg_replace('^[^[:space:]]+[[:space:]]','',$GLOBALS['TSFE']->csConvObj->crop('utf-8',$parts[$k],-($postPreLgd-$postPreLgd_offset))); 01741 } 01742 } elseif ($summaryLgd > $summaryMax || !isset($parts[$k+1])) { // In case summary length is exceed OR if there are no more entries at all: 01743 if ($strLen > $postPreLgd) { 01744 $output[$k] = ereg_replace('[[:space:]][^[:space:]]+$','',$GLOBALS['TSFE']->csConvObj->crop('utf-8',$parts[$k],$postPreLgd-$postPreLgd_offset)).$divider; 01745 } 01746 } else { // In-between search words: 01747 if ($strLen > $postPreLgd*2) { 01748 $output[$k] = ereg_replace('[[:space:]][^[:space:]]+$','',$GLOBALS['TSFE']->csConvObj->crop('utf-8',$parts[$k],$postPreLgd-$postPreLgd_offset)). 01749 $divider. 01750 ereg_replace('^[^[:space:]]+[[:space:]]','',$GLOBALS['TSFE']->csConvObj->crop('utf-8',$parts[$k],-($postPreLgd-$postPreLgd_offset))); 01751 } 01752 } 01753 $summaryLgd+= $GLOBALS['TSFE']->csConvObj->strlen('utf-8', $output[$k]);; 01754 01755 // Protect output: 01756 $output[$k] = htmlspecialchars($output[$k]); 01757 01758 // If summary lgd is exceed, break the process: 01759 if ($summaryLgd > $summaryMax) { 01760 break; 01761 } 01762 } else { 01763 $summaryLgd+= $GLOBALS['TSFE']->csConvObj->strlen('utf-8',$strP); 01764 $output[$k] = '<span class="tx-indexedsearch-redMarkup">'.htmlspecialchars($parts[$k]).'</span>'; 01765 } 01766 } 01767 01768 // Return result: 01769 return implode('',$output); 01770 } 01771 01778 function makeTitle($row) { 01779 $add = ''; 01780 01781 if ($this->multiplePagesType($row['item_type'])) { 01782 $dat = unserialize($row['cHashParams']); 01783 01784 $pp = explode('-',$dat['key']); 01785 if ($pp[0]!=$pp[1]) { 01786 $add=', '.$this->pi_getLL('word_pages').' '.$dat['key']; 01787 } else $add=', '.$this->pi_getLL('word_page').' '.$pp[0]; 01788 } 01789 01790 $outputString = $GLOBALS['TSFE']->csConvObj->crop('utf-8',$row['item_title'],50,'...'); 01791 01792 return $this->utf8_to_currentCharset(htmlspecialchars($outputString)).$add; 01793 } 01794 01802 function makeInfo($row,$tmplArray) { 01803 $tmplArray['size'] = $this->pi_getLL('res_size','',1).' '.t3lib_div::formatSize($row['item_size']).''; 01804 $tmplArray['created'] = $this->pi_getLL('res_created','',1).' '.date('d-m-y',$row['item_crdate']).''; 01805 $tmplArray['modified'] = $this->pi_getLL('res_modified','',1).' '.date('d-m-y H:i',$row['item_mtime']).''; 01806 01807 $pathId = $row['data_page_id']?$row['data_page_id']:$row['page_id']; 01808 $pathMP = $row['data_page_id']?$row['data_page_mp']:''; 01809 01810 $pI = parse_url($row['data_filename']); 01811 if ($pI['scheme']) { 01812 $tmplArray['path'] = '<a href="'.htmlspecialchars($row['data_filename']).'">'.htmlspecialchars($row['data_filename']).'</a>'; 01813 } else { 01814 $pathStr = htmlspecialchars($this->getPathFromPageId($pathId,$pathMP)); 01815 $tmplArray['path'] = $this->linkPage($pathId,htmlspecialchars($pathStr),array( 01816 'data_page_type' => $row['data_page_type'], 01817 'data_page_mp' => $pathMP, 01818 'sys_language_uid' => $row['sys_language_uid'], 01819 )); 01820 } 01821 01822 return $tmplArray; 01823 } 01824 01831 function getSpecialConfigForRow($row) { 01832 $pathId = $row['data_page_id'] ? $row['data_page_id'] : $row['page_id']; 01833 $pathMP = $row['data_page_id'] ? $row['data_page_mp'] : ''; 01834 01835 $rl = $this->getRootLine($pathId,$pathMP); 01836 $specConf = $this->conf['specConfs.']['0.']; 01837 if (is_array($rl)) { 01838 foreach($rl as $dat) { 01839 if (is_array($this->conf['specConfs.'][$dat['uid'].'.'])) { 01840 $specConf = $this->conf['specConfs.'][$dat['uid'].'.']; 01841 break; 01842 } 01843 } 01844 } 01845 01846 return $specConf; 01847 } 01848 01855 function makeLanguageIndication($row) { 01856 01857 // If search result is a TYPO3 page: 01858 if ((string)$row['item_type']==='0') { 01859 01860 // If TypoScript is used to render the flag: 01861 if (is_array($this->conf['flagRendering.'])) { 01862 $this->cObj->setCurrentVal($row['sys_language_uid']); 01863 return $this->cObj->cObjGetSingle($this->conf['flagRendering'],$this->conf['flagRendering.']); 01864 } else { // ... otherwise, get flag from sys_language record: 01865 01866 // Get sys_language record 01867 $rowDat = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('*', 'sys_language', 'uid='.intval($row['sys_language_uid']).' '.$this->cObj->enableFields('sys_language')); 01868 01869 // Flag code: 01870 $flag = $rowDat[0]['flag']; 01871 if ($flag) { 01872 $file = 't3lib/gfx/flags/'.$flag; 01873 $imgInfo = @getimagesize(PATH_site.$file); 01874 01875 if (is_array($imgInfo)) { 01876 $output = '<img src="'.$file.'" '.$imgInfo[3].' title="'.htmlspecialchars($rowDat[0]['title']).'" alt="'.htmlspecialchars($rowDat[0]['title']).'" />'; 01877 return $output; 01878 } 01879 } 01880 } 01881 } 01882 return ' '; 01883 } 01884 01892 function makeAccessIndication($id) { 01893 if (is_array($this->fe_groups_required[$id]) && count($this->fe_groups_required[$id])) { 01894 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','',1),implode(',',array_unique($this->fe_groups_required[$id]))).'" alt="" />'; 01895 } 01896 } 01897 01906 function linkPage($id,$str,$row=array(),$markUpSwParams=array()) { 01907 01908 // Parameters for link: 01909 $urlParameters = (array)unserialize($row['cHashParams']); 01910 01911 // Add &type and &MP variable: 01912 if ($row['data_page_type']) $urlParameters['type'] = $row['data_page_type']; 01913 if ($row['data_page_mp']) $urlParameters['MP'] = $row['data_page_mp']; 01914 if ($row['sys_language_uid']) $urlParameters['L'] = $row['sys_language_uid']; 01915 01916 // markup-GET vars: 01917 $urlParameters = array_merge($urlParameters, $markUpSwParams); 01918 01919 // 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... 01920 if (!is_array($this->domain_records[$id])) { 01921 $this->getPathFromPageId($id); 01922 } 01923 01924 // If external domain, then link to that: 01925 if (count($this->domain_records[$id])) { 01926 reset($this->domain_records[$id]); 01927 $firstDom = current($this->domain_records[$id]); 01928 $scheme = t3lib_div::getIndpEnv('TYPO3_SSL') ? 'https://' : 'http://'; 01929 01930 $addParams = ''; 01931 if (is_array($urlParameters)) { 01932 if (count($urlParameters)) { 01933 $addParams.= t3lib_div::implodeArrayForUrl('',$urlParameters); 01934 } 01935 } 01936 01937 return '<a href="'.$scheme.$firstDom.'/index.php?id='.$id.$addParams.'" target="'.$this->conf['search.']['detect_sys_domain_records.']['target'].'">'.htmlspecialchars($str).'</a>'; 01938 } else { 01939 return $this->pi_linkToPage($str,$id,$this->conf['result_link_target'],$urlParameters); 01940 } 01941 } 01942 01950 function getRootLine($id,$pathMP='') { 01951 $identStr = $id.'|'.$pathMP; 01952 01953 if (!isset($this->cache_path[$identStr])) { 01954 $this->cache_rl[$identStr] = $GLOBALS['TSFE']->sys_page->getRootLine($id,$pathMP); 01955 } 01956 return $this->cache_rl[$identStr]; 01957 } 01958 01965 function getFirstSysDomainRecordForPage($id) { 01966 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('domainName', 'sys_domain', 'pid='.intval($id).$this->cObj->enableFields('sys_domain'), '', 'sorting'); 01967 $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res); 01968 return ereg_replace('\/$','',$row['domainName']); 01969 } 01970 01978 function getPathFromPageId($id,$pathMP='') { 01979 01980 $identStr = $id.'|'.$pathMP; 01981 01982 if (!isset($this->cache_path[$identStr])) { 01983 $this->fe_groups_required[$id] = array(); 01984 $this->domain_records[$id] = array(); 01985 $rl = $this->getRootLine($id,$pathMP); 01986 $hitRoot = 0; 01987 $path = ''; 01988 if (is_array($rl) && count($rl)) { 01989 reset($rl); 01990 while(list($k,$v)=each($rl)) { 01991 // Check fe_user 01992 if ($v['fe_group'] && ($v['uid']==$id || $v['extendToSubpages'])) { 01993 $this->fe_groups_required[$id][]=$v['fe_group']; 01994 } 01995 // Check sys_domain. 01996 if ($this->conf['search.']['detect_sys_domain_records']) { 01997 $sysDName = $this->getFirstSysDomainRecordForPage($v['uid']); 01998 if ($sysDName) { 01999 $this->domain_records[$id][] = $sysDName; 02000 // Set path accordingly: 02001 $path = $sysDName.$path; 02002 break; 02003 } 02004 } 02005 02006 // Stop, if we find that the current id is the current root page. 02007 if ($v['uid']==$GLOBALS['TSFE']->config['rootLine'][0]['uid']) { 02008 break; 02009 } 02010 $path = '/'.$v['title'].$path; 02011 } 02012 } 02013 02014 $this->cache_path[$identStr] = $path; 02015 02016 if (is_array($this->conf['path_stdWrap.'])) { 02017 $this->cache_path[$identStr] = $this->cObj->stdWrap($this->cache_path[$identStr], $this->conf['path_stdWrap.']); 02018 } 02019 } 02020 02021 return $this->cache_path[$identStr]; 02022 } 02023 02030 function getMenu($id) { 02031 if ($this->conf['show.']['LxALLtypes']) { 02032 $output = Array(); 02033 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('title,uid', 'pages', 'pid='.intval($id).$this->cObj->enableFields('pages'), '', 'sorting'); 02034 while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { 02035 $output[$row['uid']] = $GLOBALS['TSFE']->sys_page->getPageOverlay($row); 02036 } 02037 return $output; 02038 } else { 02039 return $GLOBALS['TSFE']->sys_page->getMenu($id); 02040 } 02041 } 02042 02049 function multiplePagesType($item_type) { 02050 return is_object($this->external_parsers[$item_type]) && $this->external_parsers[$item_type]->isMultiplePageExtension($item_type); 02051 } 02052 02059 function utf8_to_currentCharset($str) { 02060 return $GLOBALS['TSFE']->csConv($str,'utf-8'); 02061 } 02062 02069 function &hookRequest($functionName) { 02070 global $TYPO3_CONF_VARS; 02071 02072 // Hook: menuConfig_preProcessModMenu 02073 if ($TYPO3_CONF_VARS['EXTCONF']['indexed_search']['pi1_hooks'][$functionName]) { 02074 $hookObj = &t3lib_div::getUserObj($TYPO3_CONF_VARS['EXTCONF']['indexed_search']['pi1_hooks'][$functionName]); 02075 if (method_exists ($hookObj, $functionName)) { 02076 $hookObj->pObj = &$this; 02077 return $hookObj; 02078 } 02079 } 02080 } 02081 } 02082 02083 02084 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/indexed_search/pi/class.tx_indexedsearch.php']) { 02085 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/indexed_search/pi/class.tx_indexedsearch.php']); 02086 } 02087 ?>