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 ***************************************************************/ 00108 require_once(PATH_tslib.'class.tslib_pibase.php'); 00109 require_once(PATH_tslib.'class.tslib_search.php'); 00110 require_once(t3lib_extMgm::extPath('indexed_search').'class.indexer.php'); 00111 00112 00123 class tx_indexedsearch extends tslib_pibase { 00124 var $prefixId = 'tx_indexedsearch'; // Same as class name 00125 var $scriptRelPath = 'pi/class.tx_indexedsearch.php'; // Path to this script relative to the extension dir. 00126 var $extKey = 'indexed_search'; // The extension key. 00127 00128 var $join_pages = 0; // See document for info about this flag... 00129 var $defaultResultNumber = 10; 00130 00131 var $operator_translate_table = Array ( // case-sensitive. Defines the words, which will be operators between words 00132 Array ('+' , 'AND'), 00133 Array ('|' , 'OR'), 00134 Array ('-' , 'AND NOT'), 00135 // english 00136 # Array ('AND' , 'AND'), 00137 # Array ('OR' , 'OR'), 00138 # Array ('NOT' , 'AND NOT'), 00139 ); 00140 00141 // Internal variable 00142 var $wholeSiteIdList = 0; // Root-page PIDs to search in (rl0 field where clause, see initialize() function) 00143 00144 // Internals: 00145 var $sWArr = array(); // Search Words and operators 00146 var $optValues = array(); // Selector box values for search configuration form 00147 var $firstRow = Array(); // Will hold the first row in result - used to calculate relative hit-ratings. 00148 00149 var $cache_path = array(); // Caching of page path 00150 var $cache_rl = array(); // Caching of root line data 00151 var $fe_groups_required = array(); // Required fe_groups memberships for display of a result. 00152 var $domain_records = array(); // Domain records (?) 00153 var $wSelClauses = array(); // Select clauses for individual words 00154 var $resultSections = array(); // Page tree sections for search result. 00155 var $external_parsers = array(); // External parser objects 00156 var $iconFileNameCache = array(); // Storage of icons.... 00157 var $lexerObj; // Lexer object 00158 var $templateCode; // Will hold the content of $conf['templateFile'] 00159 var $hiddenFieldList = 'ext, type, defOp, media, order, group, lang, desc, results'; 00160 00161 00169 function main($content, $conf) { 00170 00171 // Initialize: 00172 $this->conf = $conf; 00173 $this->pi_loadLL(); 00174 $this->pi_setPiVarDefaults(); 00175 00176 // Initialize the indexer-class - just to use a few function (for making hashes) 00177 $this->indexerObj = t3lib_div::makeInstance('tx_indexedsearch_indexer'); 00178 00179 // Initialize: 00180 $this->initialize(); 00181 00182 // Do search: 00183 // If there were any search words entered... 00184 if (is_array($this->sWArr)) { 00185 $content = $this->doSearch($this->sWArr); 00186 } 00187 00188 // Finally compile all the content, form, messages and results: 00189 $content = $this->makeSearchForm($this->optValues). 00190 $this->printRules(). 00191 $content; 00192 00193 return $this->pi_wrapInBaseClass($content); 00194 } 00195 00201 function initialize() { 00202 global $TYPO3_CONF_VARS; 00203 00204 // Initialize external document parsers for icon display and other soft operations 00205 if (is_array($TYPO3_CONF_VARS['EXTCONF']['indexed_search']['external_parsers'])) { 00206 foreach ($TYPO3_CONF_VARS['EXTCONF']['indexed_search']['external_parsers'] as $extension => $_objRef) { 00207 $this->external_parsers[$extension] = &t3lib_div::getUserObj($_objRef); 00208 00209 // Init parser and if it returns false, unset its entry again: 00210 if (!$this->external_parsers[$extension]->softInit($extension)) { 00211 unset($this->external_parsers[$extension]); 00212 } 00213 } 00214 } 00215 00216 // Init lexer (used to post-processing of search words) 00217 $lexerObjRef = $TYPO3_CONF_VARS['EXTCONF']['indexed_search']['lexer'] ? 00218 $TYPO3_CONF_VARS['EXTCONF']['indexed_search']['lexer'] : 00219 'EXT:indexed_search/class.lexer.php:&tx_indexedsearch_lexer'; 00220 $this->lexerObj = &t3lib_div::getUserObj($lexerObjRef); 00221 00222 // If "_sections" is set, this value overrides any existing value. 00223 if ($this->piVars['_sections']) $this->piVars['sections'] = $this->piVars['_sections']; 00224 00225 // If "_sections" is set, this value overrides any existing value. 00226 if ($this->piVars['_freeIndexUid']!=='_') $this->piVars['freeIndexUid'] = $this->piVars['_freeIndexUid']; 00227 00228 // Add previous search words to current 00229 if ($this->piVars['sword_prev_include'] && $this->piVars['sword_prev']) { 00230 $this->piVars['sword'] = trim($this->piVars['sword_prev']).' '.$this->piVars['sword']; 00231 } 00232 00233 $this->piVars['results'] = t3lib_div::intInRange($this->piVars['results'],1,100000,$this->defaultResultNumber); 00234 00235 // Selector-box values defined here: 00236 $this->optValues = Array( 00237 'type' => Array( 00238 '0' => $this->pi_getLL('opt_type_0'), 00239 '1' => $this->pi_getLL('opt_type_1'), 00240 '2' => $this->pi_getLL('opt_type_2'), 00241 '3' => $this->pi_getLL('opt_type_3'), 00242 '10' => $this->pi_getLL('opt_type_10'), 00243 '20' => $this->pi_getLL('opt_type_20'), 00244 ), 00245 'defOp' => Array( 00246 '0' => $this->pi_getLL('opt_defOp_0'), 00247 '1' => $this->pi_getLL('opt_defOp_1'), 00248 ), 00249 'sections' => Array( 00250 '0' => $this->pi_getLL('opt_sections_0'), 00251 '-1' => $this->pi_getLL('opt_sections_-1'), 00252 '-2' => $this->pi_getLL('opt_sections_-2'), 00253 '-3' => $this->pi_getLL('opt_sections_-3'), 00254 // 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. 00255 ), 00256 'freeIndexUid' => Array( 00257 '-1' => $this->pi_getLL('opt_freeIndexUid_-1'), 00258 '-2' => $this->pi_getLL('opt_freeIndexUid_-2'), 00259 '0' => $this->pi_getLL('opt_freeIndexUid_0'), 00260 ), 00261 'media' => Array( 00262 '-1' => $this->pi_getLL('opt_media_-1'), 00263 '0' => $this->pi_getLL('opt_media_0'), 00264 '-2' => $this->pi_getLL('opt_media_-2'), 00265 ), 00266 'order' => Array( 00267 'rank_flag' => $this->pi_getLL('opt_order_rank_flag'), 00268 'rank_freq' => $this->pi_getLL('opt_order_rank_freq'), 00269 'rank_first' => $this->pi_getLL('opt_order_rank_first'), 00270 'rank_count' => $this->pi_getLL('opt_order_rank_count'), 00271 'mtime' => $this->pi_getLL('opt_order_mtime'), 00272 'title' => $this->pi_getLL('opt_order_title'), 00273 'crdate' => $this->pi_getLL('opt_order_crdate'), 00274 ), 00275 'group' => Array ( 00276 'sections' => $this->pi_getLL('opt_group_sections'), 00277 'flat' => $this->pi_getLL('opt_group_flat'), 00278 ), 00279 'lang' => Array ( 00280 -1 => $this->pi_getLL('opt_lang_-1'), 00281 0 => $this->pi_getLL('opt_lang_0'), 00282 ), 00283 'desc' => Array ( 00284 '0' => $this->pi_getLL('opt_desc_0'), 00285 '1' => $this->pi_getLL('opt_desc_1'), 00286 ), 00287 'results' => Array ( 00288 '10' => '10', 00289 '20' => '20', 00290 '50' => '50', 00291 '100' => '100', 00292 ) 00293 ); 00294 00295 // Free Index Uid: 00296 if ($this->conf['search.']['defaultFreeIndexUidList']) { 00297 $uidList = t3lib_div::intExplode(',', $this->conf['search.']['defaultFreeIndexUidList']); 00298 $indexCfgRecords = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid,title','index_config','uid IN ('.implode(',',$uidList).')'.$this->cObj->enableFields('index_config'),'','','','uid'); 00299 00300 foreach ($uidList as $uidValue) { 00301 if (is_array($indexCfgRecords[$uidValue])) { 00302 $this->optValues['freeIndexUid'][$uidValue] = $indexCfgRecords[$uidValue]['title']; 00303 } 00304 } 00305 } 00306 00307 00308 // Add media to search in: 00309 if (strlen(trim($this->conf['search.']['mediaList']))) { 00310 $mediaList = implode(',', t3lib_div::trimExplode(',', $this->conf['search.']['mediaList'], 1)); 00311 } 00312 foreach ($this->external_parsers as $extension => $obj) { 00313 // Skip unwanted extensions 00314 if ($mediaList && !t3lib_div::inList($mediaList, $extension)) { continue; } 00315 00316 if ($name = $obj->searchTypeMediaTitle($extension)) { 00317 $this->optValues['media'][$extension] = $this->pi_getLL('opt_sections_'.$extension,$name); 00318 } 00319 } 00320 00321 // Add operators for various languages 00322 // Converts the operators to UTF-8 and lowercase 00323 $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'); 00324 $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'); 00325 $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'); 00326 00327 // This is the id of the site root. This value may be a commalist of integer (prepared for this) 00328 $this->wholeSiteIdList = intval($GLOBALS['TSFE']->config['rootLine'][0]['uid']); 00329 00330 // Creating levels for section menu: 00331 // This selects the first and secondary menus for the "sections" selector - so we can search in sections and sub sections. 00332 if ($this->conf['show.']['L1sections']) { 00333 $firstLevelMenu = $this->getMenu($this->wholeSiteIdList); 00334 while(list($kk,$mR) = each($firstLevelMenu)) { 00335 if ($mR['doktype']!=5) { 00336 $this->optValues['sections']['rl1_'.$mR['uid']] = trim($this->pi_getLL('opt_RL1').' '.$mR['title']); 00337 if ($this->conf['show.']['L2sections']) { 00338 $secondLevelMenu = $this->getMenu($mR['uid']); 00339 while(list($kk2,$mR2) = each($secondLevelMenu)) { 00340 if ($mR['doktype']!=5) { 00341 $this->optValues['sections']['rl2_'.$mR2['uid']] = trim($this->pi_getLL('opt_RL2').' '.$mR2['title']); 00342 } else unset($secondLevelMenu[$kk2]); 00343 } 00344 $this->optValues['sections']['rl2_'.implode(',',array_keys($secondLevelMenu))] = $this->pi_getLL('opt_RL2ALL'); 00345 } 00346 } else unset($firstLevelMenu[$kk]); 00347 } 00348 $this->optValues['sections']['rl1_'.implode(',',array_keys($firstLevelMenu))] = $this->pi_getLL('opt_RL1ALL'); 00349 } 00350 00351 // 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. 00352 // 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... 00353 if ($this->conf['search.']['rootPidList']) { 00354 $this->wholeSiteIdList = implode(',',t3lib_div::intExplode(',',$this->conf['search.']['rootPidList'])); 00355 } 00356 00357 // Load the template 00358 $this->templateCode = $this->cObj->fileResource($this->conf['templateFile']); 00359 00360 // Add search languages: 00361 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', 'sys_language', '1=1'.$this->cObj->enableFields('sys_language')); 00362 while($lR = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { 00363 $this->optValues['lang'][$lR['uid']] = $lR['title']; 00364 } 00365 00366 // Calling hook for modification of initialized content 00367 if ($hookObj = &$this->hookRequest('initialize_postProc')) { 00368 $hookObj->initialize_postProc(); 00369 } 00370 00371 // Default values set: 00372 // Setting first values in optValues as default values IF there is not corresponding piVar value set already. 00373 foreach ($this->optValues as $kk => $vv) { 00374 if (!isset($this->piVars[$kk])) { 00375 reset($vv); 00376 $this->piVars[$kk] = key($vv); 00377 } 00378 } 00379 00380 // Blind selectors: 00381 if (is_array($this->conf['blind.'])) { 00382 foreach ($this->conf['blind.'] as $kk => $vv) { 00383 if (is_array($vv)) { 00384 foreach ($vv as $kkk => $vvv) { 00385 if (!is_array($vvv) && $vvv && is_array($this->optValues[substr($kk,0,-1)])) { 00386 unset($this->optValues[substr($kk,0,-1)][$kkk]); 00387 } 00388 } 00389 } elseif ($vv) { // If value is not set, unset the option array. 00390 unset($this->optValues[$kk]); 00391 } 00392 } 00393 } 00394 00395 // This gets the search-words into the $sWArr: 00396 $this->sWArr = $this->getSearchWords($this->piVars['defOp']); 00397 } 00398 00414 function getSearchWords($defOp) { 00415 // 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!) 00416 $inSW = substr($this->piVars['sword'],0,200); 00417 00418 // Convert to UTF-8 + conv. entities (was also converted during indexing!) 00419 $inSW = $GLOBALS['TSFE']->csConvObj->utf8_encode($inSW, $GLOBALS['TSFE']->metaCharset); 00420 $inSW = $GLOBALS['TSFE']->csConvObj->entities_to_utf8($inSW,TRUE); 00421 00422 if ($hookObj = &$this->hookRequest('getSearchWords')) { 00423 return $hookObj->getSearchWords_splitSWords($inSW, $defOp); 00424 } else { 00425 00426 if ($this->piVars['type']==20) { 00427 return array(array('sword'=>trim($inSW), 'oper'=>'AND')); 00428 } else { 00429 $search = t3lib_div::makeInstance('tslib_search'); 00430 $search->default_operator = $defOp==1 ? 'OR' : 'AND'; 00431 $search->operator_translate_table = $this->operator_translate_table; 00432 $search->register_and_explode_search_string($inSW); 00433 00434 if (is_array($search->sword_array)) { 00435 return $this->procSearchWordsByLexer($search->sword_array); 00436 } 00437 } 00438 } 00439 } 00440 00448 function procSearchWordsByLexer($SWArr) { 00449 00450 // Init output variable: 00451 $newSWArr = array(); 00452 00453 // Traverse the search word array: 00454 foreach ($SWArr as $wordDef) { 00455 if (!strstr($wordDef['sword'],' ')) { // No space in word (otherwise it might be a sentense in quotes like "there is"). 00456 // Split the search word by lexer: 00457 $res = $this->lexerObj->split2Words($wordDef['sword']); 00458 00459 // Traverse lexer result and add all words again: 00460 foreach ($res as $word) { 00461 $newSWArr[] = array('sword'=>$word, 'oper'=>$wordDef['oper']); 00462 } 00463 } else { 00464 $newSWArr[] = $wordDef; 00465 } 00466 } 00467 00468 // Return result: 00469 return $newSWArr; 00470 } 00471 00472 00473 00474 00475 00476 00477 00478 00479 00480 /***************************** 00481 * 00482 * Main functions 00483 * 00484 *****************************/ 00485 00492 function doSearch($sWArr) { 00493 00494 // Find free index uid: 00495 $freeIndexUid = $this->piVars['freeIndexUid']; 00496 if ($freeIndexUid==-2) { 00497 $freeIndexUid = $this->conf['search.']['defaultFreeIndexUidList']; 00498 } 00499 00500 $indexCfgs = t3lib_div::intExplode(',',$freeIndexUid); 00501 $accumulatedContent = ''; 00502 00503 foreach ($indexCfgs as $freeIndexUid) { 00504 // Get result rows: 00505 $pt1 = t3lib_div::milliseconds(); 00506 if ($hookObj = &$this->hookRequest('getResultRows')) { 00507 $resData = $hookObj->getResultRows($sWArr,$freeIndexUid); 00508 } else { 00509 $resData = $this->getResultRows($sWArr,$freeIndexUid); 00510 } 00511 00512 // Display search results: 00513 $pt2 = t3lib_div::milliseconds(); 00514 if ($hookObj = &$this->hookRequest('getDisplayResults')) { 00515 $content = $hookObj->getDisplayResults($sWArr, $resData, $freeIndexUid); 00516 } else { 00517 $content = $this->getDisplayResults($sWArr, $resData, $freeIndexUid); 00518 } 00519 00520 $pt3 = t3lib_div::milliseconds(); 00521 00522 // Create header if we are searching more than one indexing configuration: 00523 if (count($indexCfgs)>1) { 00524 if ($freeIndexUid>0) { 00525 list($indexCfgRec) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('title','index_config','uid='.intval($freeIndexUid).$this->cObj->enableFields('index_config')); 00526 $titleString = $indexCfgRec['title']; 00527 } else { 00528 $titleString = $this->pi_getLL('opt_freeIndexUid_header_'.$freeIndexUid); 00529 } 00530 $content = '<h1 class="tx-indexedsearch-category">'.htmlspecialchars($titleString).'</h1>'.$content; 00531 } 00532 00533 $accumulatedContent.=$content; 00534 } 00535 00536 // Write search statistics 00537 $this->writeSearchStat($sWArr,$resData['count'],array($pt1,$pt2,$pt3)); 00538 00539 // Return content: 00540 return $accumulatedContent; 00541 } 00542 00550 function getResultRows($sWArr,$freeIndexUid=-1) { 00551 00552 // Getting SQL result pointer: 00553 $GLOBALS['TT']->push('Searching result'); 00554 $res = $this->getResultRows_SQLpointer($sWArr,$freeIndexUid); 00555 $GLOBALS['TT']->pull(); 00556 00557 // Organize and process result: 00558 if ($res) { 00559 00560 $count = $GLOBALS['TYPO3_DB']->sql_num_rows($res); // Total search-result count 00561 $pointer = t3lib_div::intInRange($this->piVars['pointer'], 0, floor($count/$this->piVars['results'])); // The pointer is set to the result page that is currently being viewed 00562 00563 // Initialize result accumulation variables: 00564 $c = 0; // Result pointer: Counts up the position in the current search-result 00565 $grouping_phashes = array(); // Used to filter out duplicates. 00566 $grouping_chashes = array(); // Used to filter out duplicates BASED ON cHash. 00567 $firstRow = Array(); // Will hold the first row in result - used to calculate relative hit-ratings. 00568 $resultRows = Array(); // Will hold the results rows for display. 00569 00570 // Now, traverse result and put the rows to be displayed into an array 00571 // Each row should contain the fields from 'ISEC.*, IP.*' combined + artificial fields "show_resume" (boolean) and "result_number" (counter) 00572 while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { 00573 00574 // Set first row: 00575 if (!$c) { 00576 $firstRow = $row; 00577 } 00578 00579 $row['show_resume'] = $this->checkResume($row); // Tells whether we can link directly to a document or not (depends on possible right problems) 00580 00581 $phashGr = !in_array($row['phash_grouping'], $grouping_phashes); 00582 $chashGr = !in_array($row['contentHash'].'.'.$row['data_page_id'], $grouping_chashes); 00583 if ($phashGr && $chashGr) { 00584 if ($row['show_resume'] || $this->conf['show.']['forbiddenRecords']) { // Only if the resume may be shown are we going to filter out duplicates... 00585 if (!$this->multiplePagesType($row['item_type'])) { // Only on documents which are not multiple pages documents 00586 $grouping_phashes[] = $row['phash_grouping']; 00587 } 00588 $grouping_chashes[] = $row['contentHash'].'.'.$row['data_page_id']; 00589 00590 $c++; // Increase the result pointer 00591 00592 // All rows for display is put into resultRows[] 00593 if ($c > $pointer * $this->piVars['results']) { 00594 $row['result_number'] = $c; 00595 $resultRows[] = $row; 00596 // This may lead to a problem: If the result check is not stopped here, the search will take longer. However the result counter will not filter out grouped cHashes/pHashes that were not processed yet. 00597 if (($c+1) > ($pointer+1)*$this->piVars['results']) break; 00598 } 00599 } else { 00600 $count--; // Skip this row if the user cannot view it (missing permission) 00601 } 00602 } else { 00603 $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. 00604 } 00605 } 00606 00607 return array( 00608 'resultRows' => $resultRows, 00609 'firstRow' => $firstRow, 00610 'count' => $count 00611 ); 00612 } else { // No results found: 00613 return FALSE; 00614 } 00615 } 00616 00624 function getResultRows_SQLpointer($sWArr,$freeIndexUid=-1) { 00625 // This SEARCHES for the searchwords in $sWArr AND returns a COMPLETE list of phash-integers of the matches. 00626 $list = $this->getPhashList($sWArr); 00627 00628 // Perform SQL Search / collection of result rows array: 00629 if ($list) { 00630 // Do the search: 00631 $GLOBALS['TT']->push('execFinalQuery'); 00632 $res = $this->execFinalQuery($list,$freeIndexUid); 00633 $GLOBALS['TT']->pull(); 00634 return $res; 00635 } else { 00636 return FALSE; 00637 } 00638 } 00639 00648 function getDisplayResults($sWArr, $resData, $freeIndexUid=-1) { 00649 // Perform display of result rows array: 00650 if ($resData) { 00651 $GLOBALS['TT']->push('Display Final result'); 00652 00653 // Set first selected row (for calculation of ranking later) 00654 $this->firstRow = $resData['firstRow']; 00655 00656 // Result display here: 00657 $rowcontent = ''; 00658 $rowcontent.= $this->compileResult($resData['resultRows'], $freeIndexUid); 00659 00660 // Browsing box: 00661 if ($resData['count']) { 00662 $this->internal['res_count'] = $resData['count']; 00663 $this->internal['results_at_a_time'] = $this->piVars['results']; 00664 $this->internal['maxPages'] = t3lib_div::intInRange($this->conf['search.']['page_links'],1,100,10); 00665 $addString = ($resData['count']&&$this->piVars['group']=='sections'&&$freeIndexUid<=0 ? ' '.sprintf($this->pi_getLL(count($this->resultSections)>1?'inNsections':'inNsection'),count($this->resultSections)):''); 00666 $browseBox1 = $this->pi_list_browseresults(1,$addString,$this->printResultSectionLinks(),$freeIndexUid); 00667 $browseBox2 = $this->pi_list_browseresults(0,'','',$freeIndexUid); 00668 } 00669 00670 // Browsing nav, bottom. 00671 if ($resData['count']) { 00672 $content = $browseBox1.$rowcontent.$browseBox2; 00673 } else { 00674 $content = '<p'.$this->pi_classParam('noresults').'>'.$this->pi_getLL('noResults','',1).'</p>'; 00675 } 00676 00677 $GLOBALS['TT']->pull(); 00678 } else { 00679 $content.='<p'.$this->pi_classParam('noresults').'>'.$this->pi_getLL('noResults','',1).'</p>'; 00680 } 00681 00682 // Print a message telling which words we searched for, and in which sections etc. 00683 $what = $this->tellUsWhatIsSeachedFor($sWArr). 00684 (substr($this->piVars['sections'],0,2)=='rl'?' '.$this->pi_getLL('inSection','',1).' "'.substr($this->getPathFromPageId(substr($this->piVars['sections'],4)),1).'"':''); 00685 $what = '<div'.$this->pi_classParam('whatis').'><p>'.$what.'</p></div>'; 00686 $content = $what.$content; 00687 00688 // Return content: 00689 return $content; 00690 } 00691 00700 function compileResult($resultRows, $freeIndexUid=-1) { 00701 $content = ''; 00702 00703 // Transfer result rows to new variable, performing some mapping of sub-results etc. 00704 $newResultRows = array(); 00705 foreach ($resultRows as $row) { 00706 $id = md5($row['phash_grouping']); 00707 if (is_array($newResultRows[$id])) { 00708 if (!$newResultRows[$id]['show_resume'] && $row['show_resume']) { // swapping: 00709 00710 // Remove old 00711 $subrows = $newResultRows[$id]['_sub']; 00712 unset($newResultRows[$id]['_sub']); 00713 $subrows[] = $newResultRows[$id]; 00714 00715 // Insert new: 00716 $newResultRows[$id] = $row; 00717 $newResultRows[$id]['_sub'] = $subrows; 00718 } else $newResultRows[$id]['_sub'][] = $row; 00719 } else { 00720 $newResultRows[$id] = $row; 00721 } 00722 } 00723 $resultRows = $newResultRows; 00724 $this->resultSections = array(); 00725 00726 if ($freeIndexUid<=0) { 00727 switch($this->piVars['group']) { 00728 case 'sections': 00729 00730 $rl2flag = substr($this->piVars['sections'],0,2)=='rl'; 00731 $sections = array(); 00732 foreach ($resultRows as $row) { 00733 $id = $row['rl0'].'-'.$row['rl1'].($rl2flag?'-'.$row['rl2']:''); 00734 $sections[$id][] = $row; 00735 } 00736 00737 $this->resultSections = array(); 00738 00739 foreach ($sections as $id => $resultRows) { 00740 $rlParts = explode('-',$id); 00741 $theId = $rlParts[2] ? $rlParts[2] : ($rlParts[1]?$rlParts[1]:$rlParts[0]); 00742 $theRLid = $rlParts[2] ? 'rl2_'.$rlParts[2]:($rlParts[1]?'rl1_'.$rlParts[1]:'0'); 00743 00744 $sectionName = $this->getPathFromPageId($theId); 00745 if ($sectionName{0} == '/') $sectionName = substr($sectionName,1); 00746 00747 if (!trim($sectionName)) { 00748 $sectionTitleLinked = $this->pi_getLL('unnamedSection','',1).':'; 00749 } else { 00750 $onclick = 'document.'.$this->prefixId.'[\''.$this->prefixId.'[_sections]\'].value=\''.$theRLid.'\';document.'.$this->prefixId.'.submit();return false;'; 00751 $sectionTitleLinked = '<a href="#" onclick="'.htmlspecialchars($onclick).'">'.htmlspecialchars($sectionName).':</a>'; 00752 } 00753 $this->resultSections[$id] = array($sectionName,count($resultRows)); 00754 00755 // Add content header: 00756 $content.= $this->makeSectionHeader($id,$sectionTitleLinked,count($resultRows)); 00757 00758 // Render result rows: 00759 foreach ($resultRows as $row) { 00760 $content.= $this->printResultRow($row); 00761 } 00762 } 00763 break; 00764 default: // flat: 00765 foreach ($resultRows as $row) { 00766 $content.= $this->printResultRow($row); 00767 } 00768 break; 00769 } 00770 } else { 00771 foreach ($resultRows as $row) { 00772 $content.= $this->printResultRow($row); 00773 } 00774 } 00775 00776 return '<div'.$this->pi_classParam('res').'>'.$content.'</div>'; 00777 } 00778 00779 00780 00781 00782 00783 00784 00785 00786 00787 00788 00789 /*********************************** 00790 * 00791 * Searching functions (SQL) 00792 * 00793 ***********************************/ 00794 00802 function getPhashList($sWArr) { 00803 00804 // Initialize variables: 00805 $c=0; 00806 $totalHashList = array(); // This array accumulates the phash-values 00807 $this->wSelClauses = array(); 00808 00809 // Traverse searchwords; for each, select all phash integers and merge/diff/intersect them with previous word (based on operator) 00810 foreach ($sWArr as $k => $v) { 00811 // Making the query for a single search word based on the search-type 00812 $sWord = $v['sword']; // $GLOBALS['TSFE']->csConvObj->conv_case('utf-8',$v['sword'],'toLower'); // lower-case all of them... 00813 $theType = (string)$this->piVars['type']; 00814 if (strstr($sWord,' ')) $theType = 20; // If there are spaces in the search-word, make a full text search instead. 00815 00816 $GLOBALS['TT']->push('SearchWord "'.$sWord.'" - $theType='.$theType); 00817 00818 $res = ''; 00819 $wSel=''; 00820 00821 // Perform search for word: 00822 switch($theType) { 00823 case '1': 00824 $wSel = "IW.baseword LIKE '%".$GLOBALS['TYPO3_DB']->quoteStr($sWord, 'index_words')."%'"; 00825 $res = $this->execPHashListQuery($wSel,' AND is_stopword=0'); 00826 break; 00827 case '2': 00828 $wSel = "IW.baseword LIKE '".$GLOBALS['TYPO3_DB']->quoteStr($sWord, 'index_words')."%'"; 00829 $res = $this->execPHashListQuery($wSel,' AND is_stopword=0'); 00830 break; 00831 case '3': 00832 $wSel = "IW.baseword LIKE '%".$GLOBALS['TYPO3_DB']->quoteStr($sWord, 'index_words')."'"; 00833 $res = $this->execPHashListQuery($wSel,' AND is_stopword=0'); 00834 break; 00835 case '10': 00836 $wSel = 'IW.metaphone = '.$this->indexerObj->metaphone($sWord); 00837 $res = $this->execPHashListQuery($wSel,' AND is_stopword=0'); 00838 break; 00839 case '20': 00840 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery( 00841 'ISEC.phash', 00842 'index_section ISEC, index_fulltext IFT', 00843 'IFT.fulltextdata LIKE \'%'.$GLOBALS['TYPO3_DB']->quoteStr($sWord, 'index_fulltext').'%\' AND 00844 ISEC.phash = IFT.phash 00845 '.$this->sectionTableWhere(), 00846 'ISEC.phash' 00847 ); 00848 $wSel = '1=1'; 00849 00850 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. 00851 break; 00852 default: 00853 $wSel = 'IW.wid = '.$hash = $this->indexerObj->md5inthash($sWord); 00854 $res = $this->execPHashListQuery($wSel,' AND is_stopword=0'); 00855 break; 00856 } 00857 00858 // Accumulate the word-select clauses 00859 $this->wSelClauses[] = $wSel; 00860 00861 // If there was a query to do, then select all phash-integers which resulted from this. 00862 if ($res) { 00863 00864 // Get phash list by searching for it: 00865 $phashList = array(); 00866 while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { 00867 $phashList[] = $row['phash']; 00868 } 00869 $GLOBALS['TYPO3_DB']->sql_free_result($res); 00870 00871 // Here the phash list are merged with the existing result based on whether we are dealing with OR, NOT or AND operations. 00872 if ($c) { 00873 switch($v['oper']) { 00874 case 'OR': 00875 $totalHashList = array_unique(array_merge($phashList,$totalHashList)); 00876 break; 00877 case 'AND NOT': 00878 $totalHashList = array_diff($totalHashList,$phashList); 00879 break; 00880 default: // AND... 00881 $totalHashList = array_intersect($totalHashList,$phashList); 00882 break; 00883 } 00884 } else { 00885 $totalHashList = $phashList; // First search 00886 } 00887 } 00888 00889 $GLOBALS['TT']->pull(); 00890 $c++; 00891 } 00892 00893 return implode(',',$totalHashList); 00894 } 00895 00903 function execPHashListQuery($wordSel,$plusQ='') { 00904 return $GLOBALS['TYPO3_DB']->exec_SELECTquery( 00905 'IR.phash', 00906 'index_words IW, 00907 index_rel IR, 00908 index_section ISEC', 00909 $wordSel.' 00910 AND IW.wid=IR.wid 00911 AND ISEC.phash = IR.phash 00912 '.$this->sectionTableWhere().' 00913 '.$plusQ, 00914 'IR.phash' 00915 ); 00916 } 00917 00923 function sectionTableWhere() { 00924 $out = $this->wholeSiteIdList<0 ? '' : 'AND ISEC.rl0 IN ('.$this->wholeSiteIdList.')'; 00925 00926 $match = ''; 00927 if (substr($this->piVars['sections'],0,4)=='rl1_') { 00928 $list = implode(',',t3lib_div::intExplode(',',substr($this->piVars['sections'],4))); 00929 $out.= 'AND ISEC.rl1 IN ('.$list.')'; 00930 $match = TRUE; 00931 } elseif (substr($this->piVars['sections'],0,4)=='rl2_') { 00932 $list = implode(',',t3lib_div::intExplode(',',substr($this->piVars['sections'],4))); 00933 $out.= 'AND ISEC.rl2 IN ('.$list.')'; 00934 $match = TRUE; 00935 } elseif (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['addRootLineFields'])) { 00936 // Traversing user configured fields to see if any of those are used to limit search to a section: 00937 foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['addRootLineFields'] as $fieldName => $rootLineLevel) { 00938 if (substr($this->piVars['sections'],0,strlen($fieldName)+1)==$fieldName.'_') { 00939 $list = implode(',',t3lib_div::intExplode(',',substr($this->piVars['sections'],strlen($fieldName)+1))); 00940 $out.= 'AND ISEC.'.$fieldName.' IN ('.$list.')'; 00941 $match = TRUE; 00942 break; 00943 } 00944 } 00945 } 00946 00947 // If no match above, test the static types: 00948 if (!$match) { 00949 switch((string)$this->piVars['sections']) { 00950 case '-1': // '-1' => 'Only this page', 00951 $out.= ' AND ISEC.page_id='.$GLOBALS['TSFE']->id; 00952 break; 00953 case '-2': // '-2' => 'Top + level 1', 00954 $out.= ' AND ISEC.rl2=0'; 00955 break; 00956 case '-3': // '-3' => 'Level 2 and out', 00957 $out.= ' AND ISEC.rl2>0'; 00958 break; 00959 } 00960 } 00961 00962 return $out; 00963 } 00964 00970 function mediaTypeWhere() { 00971 00972 switch((string)$this->piVars['media']) { 00973 case '0': // '0' => 'Kun TYPO3 sider', 00974 $out = 'AND IP.item_type='.$GLOBALS['TYPO3_DB']->fullQuoteStr('0', 'index_phash');; 00975 break; 00976 case '-2': // All external documents 00977 $out = 'AND IP.item_type!='.$GLOBALS['TYPO3_DB']->fullQuoteStr('0', 'index_phash');; 00978 break; 00979 case '-1': // All content 00980 $out=''; 00981 break; 00982 default: 00983 $out = 'AND IP.item_type='.$GLOBALS['TYPO3_DB']->fullQuoteStr($this->piVars['media'], 'index_phash'); 00984 break; 00985 } 00986 00987 return $out; 00988 } 00989 00995 function languageWhere() { 00996 if ($this->piVars['lang']>=0) { // -1 is the same as ALL language. 00997 return 'AND IP.sys_language_uid='.intval($this->piVars['lang']); 00998 } 00999 } 01000 01007 function freeIndexUidWhere($freeIndexUid) { 01008 01009 if ($freeIndexUid>=0) { 01010 01011 // First, look if the freeIndexUid is a meta configuration: 01012 list($indexCfgRec) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('indexcfgs','index_config','type=5 AND uid='.intval($freeIndexUid).$this->cObj->enableFields('index_config')); 01013 if (is_array($indexCfgRec)) { 01014 $refs = t3lib_div::trimExplode(',',$indexCfgRec['indexcfgs']); 01015 $list = array(-99); // Default value to protect against empty array. 01016 foreach ($refs as $ref) { 01017 list($table,$uid) = t3lib_div::revExplode('_',$ref,2); 01018 switch ($table) { 01019 case 'index_config': 01020 list($idxRec) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid','index_config','uid='.intval($uid).$this->cObj->enableFields('index_config')); 01021 if ($idxRec) $list[] = $uid; 01022 break; 01023 case 'pages': 01024 $indexCfgRecordsFromPid = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid','index_config','pid='.intval($uid).$this->cObj->enableFields('index_config')); 01025 foreach ($indexCfgRecordsFromPid as $idxRec) { 01026 $list[] = $idxRec['uid']; 01027 } 01028 break; 01029 } 01030 } 01031 01032 $list = array_unique($list); 01033 } else { 01034 $list = array(intval($freeIndexUid)); 01035 } 01036 01037 return ' AND IP.freeIndexUid IN ('.implode(',',$list).')'; 01038 } 01039 } 01040 01048 function execFinalQuery($list,$freeIndexUid=-1) { 01049 01050 // Setting up methods of filtering results based on page types, access, etc. 01051 $page_join = ''; 01052 $page_where = ''; 01053 01054 // Indexing configuration clause: 01055 $freeIndexUidClause = $this->freeIndexUidWhere($freeIndexUid); 01056 01057 // Calling hook for alternative creation of page ID list 01058 if ($hookObj = &$this->hookRequest('execFinalQuery_idList')) { 01059 $page_where = $hookObj->execFinalQuery_idList($list); 01060 } elseif ($this->join_pages) { // Alternative to getting all page ids by ->getTreeList() where "excludeSubpages" is NOT respected. 01061 $page_join = ', 01062 pages'; 01063 $page_where = 'pages.uid = ISEC.page_id 01064 '.$this->cObj->enableFields('pages').' 01065 AND pages.no_search=0 01066 AND pages.doktype<200 01067 '; 01068 } 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! 01069 $siteIdNumbers = t3lib_div::intExplode(',',$this->wholeSiteIdList); 01070 $id_list=array(); 01071 while(list(,$rootId)=each($siteIdNumbers)) { 01072 $id_list[] = $this->cObj->getTreeList($rootId,9999,0,0,'','').$rootId; 01073 } 01074 $page_where = 'ISEC.page_id IN ('.implode(',',$id_list).')'; 01075 } else { // Disable everything... (select all) 01076 $page_where = ' 1=1 '; 01077 } 01078 01079 01080 // 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. 01081 if (substr($this->piVars['order'],0,5)=='rank_') { 01082 /* 01083 OK there were some fancy calculations promoted by Graeme Merrall: 01084 01085 "However, regarding relevance you probably want to look at something like 01086 Salton's formula which is a good easy way to measure relevance. 01087 Oracle Intermedia uses this and it's pretty simple: 01088 Score can be between 0 and 100, but the top-scoring document in the query 01089 will not necessarily have a score of 100 -- scoring is relative, not 01090 absolute. This means that scores are not comparable across indexes, or even 01091 across different queries on the same index. Score for each document is 01092 computed using the standard Salton formula: 01093 01094 3f(1+log(N/n)) 01095 01096 Where f is the frequency of the search term in the document, N is the total 01097 number of rows in the table, and n is the number of rows which contain the 01098 search term. This is converted into an integer in the range 0 - 100. 01099 01100 There's a good doc on it at 01101 http://ls6-www.informatik.uni-dortmund.de/bib/fulltext/ir/Pfeifer:97/ 01102 although it may be a little complex for what you require so just pick the 01103 relevant parts out. 01104 " 01105 01106 However I chose not to go with this for several reasons. 01107 I do not claim that my ways of calculating importance here is the best. 01108 ANY (better) suggestion for ranking calculation is accepted! (as long as they are shipped with tested code in exchange for this.) 01109 */ 01110 01111 switch($this->piVars['order']) { 01112 case 'rank_flag': // This gives priority to word-position (max-value) so that words in title, keywords, description counts more than in content. 01113 // The ordering is refined with the frequency sum as well. 01114 $grsel = 'MAX(IR.flags) AS order_val1, SUM(IR.freq) AS order_val2'; 01115 $orderBy = 'order_val1'.$this->isDescending().',order_val2'.$this->isDescending(); 01116 break; 01117 case 'rank_first': // Results in average position of search words on page. Must be inversely sorted (low numbers are closer to top) 01118 $grsel = 'AVG(IR.first) AS order_val'; 01119 $orderBy = 'order_val'.$this->isDescending(1); 01120 break; 01121 case 'rank_count': // Number of words found 01122 $grsel = 'SUM(IR.count) AS order_val'; 01123 $orderBy = 'order_val'.$this->isDescending(); 01124 break; 01125 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? 01126 $grsel = 'SUM(IR.freq) AS order_val'; 01127 $orderBy = 'order_val'.$this->isDescending(); 01128 break; 01129 } 01130 01131 // So, words are imploded into an OR statement (no "sentence search" should be done here - may deselect results) 01132 $wordSel='('.implode(' OR ',$this->wSelClauses).') AND '; 01133 01134 return $GLOBALS['TYPO3_DB']->exec_SELECTquery( 01135 'ISEC.*, IP.*, ' 01136 .$grsel, 01137 'index_words IW, 01138 index_rel IR, 01139 index_section ISEC, 01140 index_phash IP'. 01141 $page_join, 01142 $wordSel.' 01143 IP.phash IN ('.$list.') '. 01144 $this->mediaTypeWhere().' '. 01145 $this->languageWhere(). 01146 $freeIndexUidClause.' 01147 AND IW.wid=IR.wid 01148 AND ISEC.phash = IR.phash 01149 AND IP.phash = IR.phash 01150 AND '.$page_where, 01151 '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', 01152 $orderBy 01153 ); 01154 } else { // Otherwise, if sorting are done with the pages table or other fields, there is no need for joining with the rel/word tables: 01155 01156 $orderBy = ''; 01157 switch((string)$this->piVars['order']) { 01158 case 'title': 01159 $orderBy = 'IP.item_title'.$this->isDescending(); 01160 break; 01161 case 'crdate': 01162 $orderBy = 'IP.item_crdate'.$this->isDescending(); 01163 break; 01164 case 'mtime': 01165 $orderBy = 'IP.item_mtime'.$this->isDescending(); 01166 break; 01167 } 01168 01169 return $GLOBALS['TYPO3_DB']->exec_SELECTquery( 01170 'ISEC.*, IP.*', 01171 'index_phash IP,index_section ISEC'.$page_join, 01172 'IP.phash IN ('.$list.') '. 01173 $this->mediaTypeWhere().' '. 01174 $this->languageWhere(). 01175 $freeIndexUidClause.' 01176 AND IP.phash = ISEC.phash 01177 AND '.$page_where, 01178 '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', 01179 $orderBy 01180 ); 01181 } 01182 } 01183 01191 function checkResume($row) { 01192 01193 // If the record is indexed by an indexing configuration, just show it. 01194 // At least this is needed for external URLs and files. 01195 // For records we might need to extend this - for instance block display if record is access restricted. 01196 if ($row['freeIndexUid']) { 01197 return TRUE; 01198 } 01199 01200 // Evaluate regularly indexed pages based on item_type: 01201 if ($row['item_type']) { // External media: 01202 // For external media we will check the access of the parent page on which the media was linked from. 01203 // "phash_t3" is the phash of the parent TYPO3 page row which initiated the indexing of the documents in this section. 01204 // So, selecting for the grlist records belonging to the parent phash-row where the current users gr_list exists will help us to know. 01205 // 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. 01206 $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')); 01207 if ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) { 01208 #debug("Look up for external media '".$row['data_filename']."': phash:".$row['phash_t3'].' YES - ('.$GLOBALS['TSFE']->gr_list.")!",1); 01209 return TRUE; 01210 } else { 01211 #debug("Look up for external media '".$row['data_filename']."': phash:".$row['phash_t3'].' NO - ('.$GLOBALS['TSFE']->gr_list.")!",1); 01212 return FALSE; 01213 } 01214 } else { // Ordinary TYPO3 pages: 01215 if (strcmp($row['gr_list'],$GLOBALS['TSFE']->gr_list)) { 01216 // 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... 01217 $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')); 01218 if ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) { 01219 #debug('Checking on it ...'.$row['item_title'].'/'.$row['phash'].' - YES ('.$GLOBALS['TSFE']->gr_list.")",1); 01220 return TRUE; 01221 } else { 01222 #debug('Checking on it ...'.$row['item_title'].'/'.$row['phash']." - NOPE",1); 01223 return FALSE; 01224 } 01225 } else { 01226 #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); 01227 return TRUE; 01228 } 01229 } 01230 } 01231 01238 function isDescending($inverse=FALSE) { 01239 $desc = $this->piVars['desc']; 01240 if ($inverse) $desc=!$desc; 01241 return !$desc ? ' DESC':''; 01242 } 01243 01252 function writeSearchStat($sWArr,$count,$pt) { 01253 $insertFields = array( 01254 'searchstring' => $this->piVars['sword'], 01255 'searchoptions' => serialize(array($this->piVars,$sWArr,$pt)), 01256 'feuser_id' => intval($this->fe_user->user['uid']), // fe_user id, integer 01257 'cookie' => $this->fe_user->id, // cookie as set or retrieve. If people has cookies disabled this will vary all the time... 01258 'IP' => t3lib_div::getIndpEnv('REMOTE_ADDR'), // Remote IP address 01259 'hits' => intval($count), // Number of hits on the search. 01260 'tstamp' => $GLOBALS['EXEC_TIME'] // Time stamp 01261 ); 01262 01263 $GLOBALS['TYPO3_DB']->exec_INSERTquery('index_stat_search', $insertFields); 01264 $newId = $GLOBALS['TYPO3_DB']->sql_insert_id(); 01265 01266 if ($newId) { 01267 foreach ($sWArr as $val) { 01268 $insertFields = array( 01269 'word' => $val['sword'], // $GLOBALS['TSFE']->csConvObj->conv_case('utf-8', $val['sword'], 'toLower'), 01270 'index_stat_search_id' => $newId, 01271 'tstamp' => $GLOBALS['EXEC_TIME'], // Time stamp 01272 'pageid' => $GLOBALS['TSFE']->id //search page id for indexed search stats 01273 ); 01274 01275 $GLOBALS['TYPO3_DB']->exec_INSERTquery('index_stat_word', $insertFields); 01276 } 01277 } 01278 } 01279 01280 01281 01282 01283 01284 01285 01286 01287 01288 01289 01290 01291 01292 /*********************************** 01293 * 01294 * HTML output functions 01295 * 01296 ***********************************/ 01297 01304 function makeSearchForm($optValues) { 01305 $html = $this->cObj->getSubpart($this->templateCode, '###SEARCH_FORM###'); 01306 01307 // Multilangual text 01308 $substituteArray = array('searchFor', 'extResume', 'atATime', 'orderBy', 'fromSection', 'searchIn', 'match', 'style', 'freeIndexUid'); 01309 foreach ($substituteArray as $marker) { 01310 $markerArray['###FORM_'.strtoupper($marker).'###'] = $this->pi_getLL('form_'.$marker,'',1); 01311 } 01312 01313 $markerArray['###FORM_SUBMIT###'] = $this->pi_getLL('submit_button_label','',1); 01314 01315 // Adding search field value 01316 $markerArray['###SWORD_VALUE###'] = htmlspecialchars($this->piVars['sword']); 01317 01318 // Additonal keyword => "Add to current search words" 01319 if ($this->conf['show.']['clearSearchBox'] && $this->conf['show.']['clearSearchBox.']['enableSubSearchCheckBox']) { 01320 $markerArray['###SWORD_PREV_VALUE###'] = htmlspecialchars($this->conf['show.']['clearSearchBox'] ? '' : $this->piVars['sword']); 01321 $markerArray['###SWORD_PREV_INCLUDE_CHECKED###'] = $this->piVars['sword_prev_include'] ? ' checked="checked"':''; 01322 $markerArray['###ADD_TO_CURRENT_SEARCH###'] = $this->pi_getLL('makerating_addToCurrentSearch','',1); 01323 } else { 01324 $html = $this->cObj->substituteSubpart($html, '###ADDITONAL_KEYWORD###', ''); 01325 } 01326 01327 $markerArray['###ACTION_URL###'] = $this->pi_getPageLink($GLOBALS['TSFE']->id, $GLOBALS['TSFE']->sPre); 01328 01329 $hiddenFieldCode = $this->cObj->getSubpart($this->templateCode, '###HIDDEN_FIELDS###'); 01330 $hiddenFieldCode = preg_replace('/^\n\t(.+)/ms', '$1', $hiddenFieldCode); // Remove first newline and tab (cosmetical issue) 01331 $hiddenFieldArr = array(); 01332 01333 foreach (t3lib_div::trimExplode(',',$this->hiddenFieldList) as $fieldName) { 01334 $hiddenFieldMarkerArray = array(); 01335 $hiddenFieldMarkerArray['###HIDDEN_FIELDNAME###'] = $this->prefixId.'['.$fieldName.']'; 01336 $hiddenFieldMarkerArray['###HIDDEN_VALUE###'] = (string)$this->piVars[$fieldName]; 01337 01338 $hiddenFieldArr[$fieldName] = $this->cObj->substituteMarkerArrayCached($hiddenFieldCode, $hiddenFieldMarkerArray, array(), array()); 01339 } 01340 01341 // Extended search 01342 if ($this->piVars['ext']) { 01343 01344 // Search for 01345 if ((!is_array($optValues['type']) && !is_array($optValues['defOp'])) || ($this->conf['blind.']['type'] && $this->conf['blind.']['defOp'])) { 01346 $html = $this->cObj->substituteSubpart($html, '###SELECT_SEARCH_FOR###', ''); 01347 } else { 01348 if (is_array($optValues['type']) && !$this->conf['blind.']['type']) { 01349 unset($hiddenFieldArr['type']); 01350 $markerArray['###SELECTBOX_TYPE_VALUES###'] = $this->renderSelectBoxValues($this->piVars['type'],$optValues['type']); 01351 } else { 01352 $html = $this->cObj->substituteSubpart($html, '###SELECT_SEARCH_TYPE###', ''); 01353 } 01354 01355 if (is_array($optValues['defOp']) || !$this->conf['blind.']['defOp']) { 01356 $markerArray['###SELECTBOX_DEFOP_VALUES###'] = $this->renderSelectBoxValues($this->piVars['defOp'],$optValues['defOp']); 01357 } else { 01358 $html = $this->cObj->substituteSubpart($html, '###SELECT_SEARCH_DEFOP###', ''); 01359 } 01360 } 01361 01362 // Search in 01363 if ((!is_array($optValues['media']) && !is_array($optValues['lang'])) || ($this->conf['blind.']['media'] && $this->conf['blind.']['lang'])) { 01364 $html = $this->cObj->substituteSubpart($html, '###SELECT_SEARCH_IN###', ''); 01365 } else { 01366 if (is_array($optValues['media']) && !$this->conf['blind.']['media']) { 01367 unset($hiddenFieldArr['media']); 01368 $markerArray['###SELECTBOX_MEDIA_VALUES###'] = $this->renderSelectBoxValues($this->piVars['media'],$optValues['media']); 01369 } else { 01370 $html = $this->cObj->substituteSubpart($html, '###SELECT_SEARCH_MEDIA###', ''); 01371 } 01372 01373 if (is_array($optValues['lang']) || !$this->conf['blind.']['lang']) { 01374 unset($hiddenFieldArr['lang']); 01375 $markerArray['###SELECTBOX_LANG_VALUES###'] = $this->renderSelectBoxValues($this->piVars['lang'],$optValues['lang']); 01376 } else { 01377 $html = $this->cObj->substituteSubpart($html, '###SELECT_SEARCH_LANG###', ''); 01378 } 01379 } 01380 01381 // Sections 01382 if (!is_array($optValues['sections']) || $this->conf['blind.']['sections']) { 01383 $html = $this->cObj->substituteSubpart($html, '###SELECT_SECTION###', ''); 01384 } else { 01385 $markerArray['###SELECTBOX_SECTIONS_VALUES###'] = $this->renderSelectBoxValues($this->piVars['sections'],$optValues['sections']); 01386 } 01387 01388 // Free Indexing Configurations: 01389 if (!is_array($optValues['freeIndexUid']) || $this->conf['blind.']['freeIndexUid']) { 01390 $html = $this->cObj->substituteSubpart($html, '###SELECT_FREEINDEXUID###', ''); 01391 } else { 01392 $markerArray['###SELECTBOX_FREEINDEXUIDS_VALUES###'] = $this->renderSelectBoxValues($this->piVars['freeIndexUid'],$optValues['freeIndexUid']); 01393 } 01394 01395 // Sorting 01396 if (!is_array($optValues['order']) || !is_array($optValues['desc']) || $this->conf['blind.']['order']) { 01397 $html = $this->cObj->substituteSubpart($html, '###SELECT_ORDER###', ''); 01398 } else { 01399 unset($hiddenFieldArr['order']); 01400 unset($hiddenFieldArr['desc']); 01401 unset($hiddenFieldArr['results']); 01402 $markerArray['###SELECTBOX_ORDER_VALUES###'] = $this->renderSelectBoxValues($this->piVars['order'],$optValues['order']); 01403 $markerArray['###SELECTBOX_DESC_VALUES###'] = $this->renderSelectBoxValues($this->piVars['desc'],$optValues['desc']); 01404 $markerArray['###SELECTBOX_RESULTS_VALUES###'] = $this->renderSelectBoxValues($this->piVars['results'],$optValues['results']); 01405 } 01406 01407 // Limits 01408 if (!is_array($optValues['results']) || !is_array($optValues['results']) || $this->conf['blind.']['results']) { 01409 $html = $this->cObj->substituteSubpart($html, '###SELECT_RESULTS###', ''); 01410 } else { 01411 $markerArray['###SELECTBOX_RESULTS_VALUES###'] = $this->renderSelectBoxValues($this->piVars['results'],$optValues['results']); 01412 } 01413 01414 // Grouping 01415 if (!is_array($optValues['group']) || $this->conf['blind.']['group']) { 01416 $html = $this->cObj->substituteSubpart($html, '###SELECT_GROUP###', ''); 01417 } else { 01418 unset($hiddenFieldArr['group']); 01419 $markerArray['###SELECTBOX_GROUP_VALUES###'] = $this->renderSelectBoxValues($this->piVars['group'],$optValues['group']); 01420 } 01421 01422 if ($this->conf['blind.']['extResume']) { 01423 $html = $this->cObj->substituteSubpart($html, '###SELECT_EXTRESUME###', ''); 01424 } else { 01425 $markerArray['###EXT_RESUME_CHECKED###'] = $this->piVars['extResume'] ? ' checked="checked"' : ''; 01426 } 01427 01428 } else { // Extended search 01429 $html = $this->cObj->substituteSubpart($html, '###SEARCH_FORM_EXTENDED###', ''); 01430 } 01431 01432 if($this->conf['show.']['advancedSearchLink']) { 01433 $linkToOtherMode = ($this->piVars['ext'] ? 01434 $this->pi_getPageLink($GLOBALS['TSFE']->id,$GLOBALS['TSFE']->sPre,array($this->prefixId.'[ext]'=>0)) : 01435 $this->pi_getPageLink($GLOBALS['TSFE']->id,$GLOBALS['TSFE']->sPre,array($this->prefixId.'[ext]'=>1)) 01436 ); 01437 01438 $markerArray['###LINKTOOTHERMODE###'] = '<a href="'.htmlspecialchars($linkToOtherMode).'">'.$this->pi_getLL($this->piVars['ext']?'link_regularSearch':'link_advancedSearch', '', 1).'</a>'; 01439 } else { 01440 $markerArray['###LINKTOOTHERMODE###'] = ''; 01441 } 01442 01443 // Write all hidden fields 01444 $html = $this->cObj->substituteSubpart($html, '###HIDDEN_FIELDS###', implode('',$hiddenFieldArr)); 01445 01446 $substitutedContent = $this->cObj->substituteMarkerArrayCached($html, $markerArray, array(), array()); 01447 01448 return $substitutedContent; 01449 } 01450 01458 function renderSelectBoxValues($value,$optValues) { 01459 if (is_array($optValues)) { 01460 $opt=array(); 01461 $isSelFlag=0; 01462 foreach ($optValues as $k=>$v) { 01463 $sel = (!strcmp($k,$value) ? ' selected="selected"' : ''); 01464 if ($sel) { $isSelFlag++; } 01465 $opt[]='<option value="'.htmlspecialchars($k).'"'.$sel.'>'.htmlspecialchars($v).'</option>'; 01466 } 01467 01468 return implode('',$opt); 01469 } 01470 } 01471 01477 function printRules() { 01478 if ($this->conf['show.']['rules']) { 01479 01480 $html = $this->cObj->getSubpart($this->templateCode, '###RULES###'); 01481 01482 $markerArray['###RULES_HEADER###'] = $this->pi_getLL('rules_header','',1); 01483 $markerArray['###RULES_TEXT###'] = nl2br(trim($this->pi_getLL('rules_text','',1))); 01484 01485 $substitutedContent = $this->cObj->substituteMarkerArrayCached($html, $markerArray, array(), array()); 01486 01487 return '<div'.$this->pi_classParam('rules').'>'.$this->cObj->stdWrap($substitutedContent, $this->conf['rules_stdWrap.']).'</div>'; 01488 } 01489 } 01490 01496 function printResultSectionLinks() { 01497 if (count($this->resultSections)) { 01498 $lines = array(); 01499 01500 $html = $this->cObj->getSubpart($this->templateCode, '###RESULT_SECTION_LINKS###'); 01501 $item = $this->cObj->getSubpart($this->templateCode, '###RESULT_SECTION_LINKS_LINK###'); 01502 01503 foreach ($this->resultSections as $id => $dat) { 01504 $markerArray = array(); 01505 01506 $aBegin = '<a href="'.htmlspecialchars($GLOBALS['TSFE']->anchorPrefix.'#anchor_'.md5($id)).'">'; 01507 $aContent = htmlspecialchars(trim($dat[0]) ? trim($dat[0]) : $this->pi_getLL('unnamedSection')). 01508 ' ('.$dat[1].' '.$this->pi_getLL($dat[1]>1 ? 'word_pages' : 'word_page','',1).')'; 01509 $aEnd = '</a>'; 01510 01511 $markerArray['###LINK###'] = $aBegin . $aContent . $aEnd; 01512 01513 $links[] = $this->cObj->substituteMarkerArrayCached($item, $markerArray, array(), array()); 01514 } 01515 01516 $html = $this->cObj->substituteMarkerArrayCached($html, array('###LINKS###' => implode('',$links)), array(), array()); 01517 01518 return '<div'.$this->pi_classParam('sectionlinks').'>'.$this->cObj->stdWrap($html, $this->conf['sectionlinks_stdWrap.']).'</div>'; 01519 } 01520 } 01521 01530 function makeSectionHeader($id, $sectionTitleLinked, $countResultRows) { 01531 01532 $html = $this->cObj->getSubpart($this->templateCode, '###SECTION_HEADER###'); 01533 01534 $markerArray['###ANCHOR_URL###'] = 'anchor_'.md5($id); 01535 $markerArray['###SECTION_TITLE###'] = $sectionTitleLinked; 01536 $markerArray['###RESULT_COUNT###'] = $countResultRows; 01537 $markerArray['###RESULT_NAME###'] = $this->pi_getLL('word_page'.($countResultRows>1 ? 's' : '')); 01538 01539 $substitutedContent = $this->cObj->substituteMarkerArrayCached($html, $markerArray, array(), array()); 01540 01541 return $substitutedContent; 01542 } 01543 01551 function printResultRow($row, $headerOnly=0) { 01552 01553 // Get template content: 01554 $tmplContent = $this->prepareResultRowTemplateData($row, $headerOnly); 01555 01556 if ($hookObj = &$this->hookRequest('printResultRow')) { 01557 return $hookObj->printResultRow($row, $headerOnly, $tmplContent); 01558 } else { 01559 01560 $html = $this->cObj->getSubpart($this->templateCode, '###RESULT_OUTPUT###'); 01561 01562 if (!is_array($row['_sub'])) { 01563 $html = $this->cObj->substituteSubpart($html, '###ROW_SUB###', ''); 01564 } 01565 01566 if (!$headerOnly) { 01567 $html = $this->cObj->substituteSubpart($html, '###ROW_SHORT###', ''); 01568 01569 } elseif ($headerOnly==1) { 01570 $html = $this->cObj->substituteSubpart($html, '###ROW_LONG###', ''); 01571 } elseif ($headerOnly==2) { 01572 $html = $this->cObj->substituteSubpart($html, '###ROW_SHORT###', ''); 01573 $html = $this->cObj->substituteSubpart($html, '###ROW_LONG###', ''); 01574 } 01575 01576 if (is_array($tmplContent)) { 01577 foreach ($tmplContent AS $k => $v) { 01578 $markerArray['###'.strtoupper($k).'###'] = $v; 01579 } 01580 } 01581 01582 // Description text 01583 $markerArray['###TEXT_ITEM_SIZE###'] = $this->pi_getLL('res_size','',1); 01584 $markerArray['###TEXT_ITEM_CRDATE###'] = $this->pi_getLL('res_created','',1); 01585 $markerArray['###TEXT_ITEM_MTIME###'] = $this->pi_getLL('res_modified','',1); 01586 $markerArray['###TEXT_ITEM_PATH###'] = $this->pi_getLL('res_path','',1); 01587 01588 $html = $this->cObj->substituteMarkerArrayCached($html, $markerArray, array(), array()); 01589 01590 // If there are subrows (eg. subpages in a PDF-file or if a duplicate page is selected due to user-login (phash_grouping)) 01591 if (is_array($row['_sub'])) { 01592 if ($this->multiplePagesType($row['item_type'])) { 01593 01594 $html = str_replace('###TEXT_ROW_SUB###', $this->pi_getLL('res_otherMatching','',1), $html); 01595 01596 foreach ($row['_sub'] as $subRow) { 01597 $html .= $this->printResultRow($subRow,1); 01598 } 01599 } else { 01600 01601 $markerArray['###TEXT_ROW_SUB###'] = $this->pi_getLL('res_otherMatching','',1); 01602 01603 $html = str_replace('###TEXT_ROW_SUB###', $this->pi_getLL('res_otherPageAsWell','',1), $html); 01604 } 01605 } 01606 01607 return $html; 01608 } 01609 } 01610 01620 function pi_list_browseresults($showResultCount=1,$addString='',$addPart='',$freeIndexUid=-1) { 01621 01622 // Initializing variables: 01623 $pointer=$this->piVars['pointer']; 01624 $count=$this->internal['res_count']; 01625 $results_at_a_time = t3lib_div::intInRange($this->internal['results_at_a_time'],1,1000); 01626 $maxPages = t3lib_div::intInRange($this->internal['maxPages'],1,100); 01627 $pageCount = ceil($count/$results_at_a_time); 01628 $sTables = ''; 01629 01630 if ($pageCount > 1) { // only show the result browser if more than one page is needed 01631 $pointer=intval($pointer); 01632 $links=array(); 01633 01634 // Make browse-table/links: 01635 if ($pointer>0) { // all pages after the 1st one 01636 $links[]='<li>'.$this->makePointerSelector_link($this->pi_getLL('pi_list_browseresults_prev','< Previous',1),$pointer-1,$freeIndexUid).'</li>'; 01637 } 01638 01639 for($a=0;$a<$pageCount;$a++) { 01640 $min = max(0, $pointer+1-ceil($maxPages/2)); 01641 $max = $min+$maxPages; 01642 if($max>$pageCount) { 01643 $min = $min - ($max-$pageCount); 01644 } 01645 01646 if($a >= $min && $a < $max) { 01647 if($a==$pointer) { 01648 $links[]='<li'.$this->pi_classParam('browselist-currentPage').'><strong>'.$this->makePointerSelector_link(trim($this->pi_getLL('pi_list_browseresults_page','Page',1).' '.($a+1)),$a,$freeIndexUid).'</strong></li>'; 01649 } else { 01650 $links[]='<li>'.$this->makePointerSelector_link(trim($this->pi_getLL('pi_list_browseresults_page','Page',1).' '.($a+1)),$a,$freeIndexUid).'</li>'; 01651 } 01652 } 01653 } 01654 if ($pointer+1 < $pageCount) { 01655 $links[]='<li>'.$this->makePointerSelector_link($this->pi_getLL('pi_list_browseresults_next','Next >',1),$pointer+1,$freeIndexUid).'</li>'; 01656 } 01657 } 01658 01659 $pR1 = $pointer*$results_at_a_time+1; 01660 $pR2 = $pointer*$results_at_a_time+$results_at_a_time; 01661 if(is_array($links)) { 01662 $addPart .= ' 01663 <ul class="browsebox"> 01664 '.implode('',$links).' 01665 </ul>'; 01666 } 01667 01668 $label = $this->pi_getLL('pi_list_browseresults_display','Displaying results ###TAG_BEGIN###%s to %s###TAG_END### out of ###TAG_BEGIN###%s###TAG_END###'); 01669 $label = str_replace('###TAG_BEGIN###','<strong>',$label); 01670 $label = str_replace('###TAG_END###','</strong>',$label); 01671 01672 $sTables = '<div'.$this->pi_classParam('browsebox').'>'. 01673 ($showResultCount ? '<p>'.sprintf( 01674 $label, 01675 $pR1, 01676 min(array($this->internal['res_count'],$pR2)), 01677 $this->internal['res_count'] 01678 ).$addString.'</p>':'' 01679 ).$addPart.'</div>'; 01680 01681 return $sTables; 01682 } 01683 01684 01685 01686 01687 01688 01689 01690 01691 01692 01693 01694 01695 /*********************************** 01696 * 01697 * Support functions for HTML output (with a minimum of fixed markup) 01698 * 01699 ***********************************/ 01700 01708 function prepareResultRowTemplateData($row, $headerOnly) { 01709 01710 // Initialize: 01711 $specRowConf = $this->getSpecialConfigForRow($row); 01712 $CSSsuffix = $specRowConf['CSSsuffix']?'-'.$specRowConf['CSSsuffix']:''; 01713 01714 // If external media, link to the media-file instead. 01715 if ($row['item_type']) { // External media 01716 if ($row['show_resume']) { // Can link directly. 01717 $title = '<a href="'.htmlspecialchars($row['data_filename']).'">'.htmlspecialchars($this->makeTitle($row)).'</a>'; 01718 } else { // Suspicious, so linking to page instead... 01719 $copy_row = $row; 01720 unset($copy_row['cHashParams']); 01721 $title = $this->linkPage($row['page_id'],htmlspecialchars($this->makeTitle($row)),$copy_row); 01722 } 01723 } else { // Else the page: 01724 01725 // Prepare search words for markup in content: 01726 if ($this->conf['forwardSearchWordsInResultLink']) { 01727 $markUpSwParams = array('no_cache' => 1); 01728 foreach ($this->sWArr as $d) { 01729 $markUpSwParams['sword_list'][] = $d['sword']; 01730 } 01731 } else { 01732 $markUpSwParams = array(); 01733 } 01734 $title = $this->linkPage($row['data_page_id'],htmlspecialchars($this->makeTitle($row)),$row,$markUpSwParams); 01735 } 01736 01737 $tmplContent = array(); 01738 $tmplContent['title'] = $title; 01739 $tmplContent['result_number'] = $this->conf['show.']['resultNumber'] ? $row['result_number'].': ' : ' '; 01740 $tmplContent['icon'] = $this->makeItemTypeIcon($row['item_type'],'',$specRowConf); 01741 $tmplContent['rating'] = $this->makeRating($row); 01742 $tmplContent['description'] = $this->makeDescription($row,$this->piVars['extResume'] && !$headerOnly?0:1); 01743 $tmplContent = $this->makeInfo($row,$tmplContent); 01744 $tmplContent['access'] = $this->makeAccessIndication($row['page_id']); 01745 $tmplContent['language'] = $this->makeLanguageIndication($row); 01746 $tmplContent['CSSsuffix'] = $CSSsuffix; 01747 01748 // Post processing with hook. 01749 if ($hookObj = &$this->hookRequest('prepareResultRowTemplateData_postProc')) { 01750 $tmplContent = $hookObj->prepareResultRowTemplateData_postProc($tmplContent, $row, $headerOnly); 01751 } 01752 01753 return $tmplContent; 01754 } 01755 01762 function tellUsWhatIsSeachedFor($sWArr) { 01763 01764 // Init: 01765 $searchingFor = ''; 01766 $c=0; 01767 01768 // Traverse search words: 01769 foreach ($sWArr as $k => $v) { 01770 if ($c) { 01771 switch($v['oper']) { 01772 case 'OR': 01773 $searchingFor.= ' '.$this->pi_getLL('searchFor_or','',1).' '.$this->wrapSW($this->utf8_to_currentCharset($v['sword'])); 01774 break; 01775 case 'AND NOT': 01776 $searchingFor.= ' '.$this->pi_getLL('searchFor_butNot','',1).' '.$this->wrapSW($this->utf8_to_currentCharset($v['sword'])); 01777 break; 01778 default: // AND... 01779 $searchingFor.= ' '.$this->pi_getLL('searchFor_and','',1).' '.$this->wrapSW($this->utf8_to_currentCharset($v['sword'])); 01780 break; 01781 } 01782 } else { 01783 $searchingFor = $this->pi_getLL('searchFor','',1).' '.$this->wrapSW($this->utf8_to_currentCharset($v['sword'])); 01784 } 01785 $c++; 01786 } 01787 return $searchingFor; 01788 } 01789 01796 function wrapSW($str) { 01797 return '"<span'.$this->pi_classParam('sw').'>'.htmlspecialchars($str).'</span>"'; 01798 } 01799 01808 function renderSelectBox($name,$value,$optValues) { 01809 if (is_array($optValues)) { 01810 $opt = array(); 01811 $isSelFlag = 0; 01812 01813 foreach ($optValues as $k => $v) { 01814 $sel = (!strcmp($k,$value) ? ' selected="selected"' : ''); 01815 if ($sel) $isSelFlag++; 01816 $opt[] = '<option value="'.htmlspecialchars($k).'"'.$sel.'>'.htmlspecialchars($v).'</option>'; 01817 } 01818 01819 return '<select name="'.$name.'">'.implode('',$opt).'</select>'; 01820 } 01821 } 01822 01832 function makePointerSelector_link($str,$p,$freeIndexUid) { 01833 $onclick = 'document.'.$this->prefixId.'[\''.$this->prefixId.'[pointer]\'].value=\''.$p.'\';'. 01834 'document.'.$this->prefixId.'[\''.$this->prefixId.'[_freeIndexUid]\'].value=\''.rawurlencode($freeIndexUid).'\';'. 01835 'document.'.$this->prefixId.'.submit();return false;'; 01836 return '<a href="#" onclick="'.htmlspecialchars($onclick).'">'.$str.'</a>'; 01837 } 01838 01847 function makeItemTypeIcon($it,$alt='',$specRowConf) { 01848 if (!isset($this->iconFileNameCache[$it])) { 01849 $this->iconFileNameCache[$it] = ''; 01850 01851 // If TypoScript is used to render the icon: 01852 if (is_array($this->conf['iconRendering.'])) { 01853 $this->cObj->setCurrentVal($it); 01854 $this->iconFileNameCache[$it] = $this->cObj->cObjGetSingle($this->conf['iconRendering'],$this->conf['iconRendering.']); 01855 } else { // ... otherwise, get flag from sys_language record: 01856 01857 // Default creation / finding of icon: 01858 $icon = ''; 01859 if ($it==='0') { 01860 if (is_array($specRowConf['pageIcon.'])) { 01861 $this->iconFileNameCache[$it] = $this->cObj->IMAGE($specRowConf['pageIcon.']); 01862 } else { 01863 $icon = 'EXT:indexed_search/pi/res/pages.gif'; 01864 } 01865 } elseif ($this->external_parsers[$it]) { 01866 $icon = $this->external_parsers[$it]->getIcon($it); 01867 } 01868 01869 if ($icon) { 01870 $fullPath = t3lib_div::getFileAbsFileName($icon); 01871 01872 if ($fullPath) { 01873 $info = @getimagesize($fullPath); 01874 $iconPath = substr($fullPath,strlen(PATH_site)); 01875 $this->iconFileNameCache[$it] = is_array($info) ? '<img src="'.$iconPath.'" '.$info[3].' title="'.htmlspecialchars($alt).'" alt="" />' : ''; 01876 } 01877 } 01878 } 01879 } 01880 return $this->iconFileNameCache[$it]; 01881 } 01882 01889 function makeRating($row) { 01890 01891 switch((string)$this->piVars['order']) { 01892 case 'rank_count': // Number of occurencies on page 01893 return $row['order_val'].' '.$this->pi_getLL('maketitle_matches'); 01894 break; 01895 case 'rank_first': // Close to top of page 01896 return ceil(t3lib_div::intInRange(255-$row['order_val'],1,255)/255*100).'%'; 01897 break; 01898 case 'rank_flag': // Based on priority assigned to <title> / <meta-keywords> / <meta-description> / <body> 01899 if ($this->firstRow['order_val2']) { 01900 $base = $row['order_val1']*256; // (3 MSB bit, 224 is highest value of order_val1 currently) 01901 $freqNumber = $row['order_val2']/$this->firstRow['order_val2']*pow(2,12); // 15-3 MSB = 12 01902 $total = t3lib_div::intInRange($base+$freqNumber,0,32767); 01903 #debug($total); 01904 return ceil(log($total)/log(32767)*100).'%'; 01905 } 01906 break; 01907 case 'rank_freq': // Based on frequency 01908 $max = 10000; 01909 $total = t3lib_div::intInRange($row['order_val'],0,$max); 01910 # debug($total); 01911 return ceil(log($total)/log($max)*100).'%'; 01912 break; 01913 case 'crdate': // Based on creation date 01914 return $this->cObj->calcAge(time()-$row['item_crdate'],0); // ,$conf['age'] 01915 break; 01916 case 'mtime': // Based on modification time 01917 return $this->cObj->calcAge(time()-$row['item_mtime'],0); // ,$conf['age'] 01918 break; 01919 default: // fx. title 01920 return ' '; 01921 break; 01922 } 01923 } 01924 01933 function makeDescription($row,$noMarkup=0,$lgd=180) { 01934 if ($row['show_resume']) { 01935 if (!$noMarkup) { 01936 $markedSW = ''; 01937 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', 'index_fulltext', 'phash='.intval($row['phash'])); 01938 if ($ftdrow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { 01939 // Cut HTTP references after some length 01940 $content = preg_replace('/(http:\/\/[^ ]{60})([^ ]+)/i', '$1...', $ftdrow['fulltextdata']); 01941 $markedSW = $this->markupSWpartsOfString($content); 01942 } 01943 $GLOBALS['TYPO3_DB']->sql_free_result($res); 01944 } 01945 01946 if (!trim($markedSW)) { 01947 $outputStr = $GLOBALS['TSFE']->csConvObj->crop('utf-8',$row['item_description'],$lgd); 01948 $outputStr = htmlspecialchars($outputStr); 01949 } 01950 $output = $this->utf8_to_currentCharset($outputStr ? $outputStr : $markedSW); 01951 } else { 01952 $output = '<span class="noResume">'.$this->pi_getLL('res_noResume','',1).'</span>'; 01953 } 01954 01955 return $output; 01956 } 01957 01964 function markupSWpartsOfString($str) { 01965 01966 // Init: 01967 $str = str_replace(' ',' ',t3lib_parsehtml::bidir_htmlspecialchars($str,-1)); 01968 $str = preg_replace('/\s\s+/',' ',$str); 01969 $swForReg = array(); 01970 01971 // Prepare search words for regex: 01972 foreach ($this->sWArr as $d) { 01973 $swForReg[] = quotemeta($d['sword']); 01974 } 01975 $regExString = '('.implode('|',$swForReg).')'; 01976 01977 // Split and combine: 01978 $parts = preg_split('/'.$regExString.'/i', ' '.$str.' ', 20000, PREG_SPLIT_DELIM_CAPTURE); 01979 #debug($parts,$regExString); 01980 // Constants: 01981 $summaryMax = 300; 01982 $postPreLgd = 60; 01983 $postPreLgd_offset = 5; 01984 $divider = ' ... '; 01985 01986 $occurencies = (count($parts)-1)/2; 01987 if ($occurencies) { 01988 $postPreLgd = t3lib_div::intInRange($summaryMax/$occurencies,$postPreLgd,$summaryMax/2); 01989 } 01990 01991 // Variable: 01992 $summaryLgd = 0; 01993 $output = array(); 01994 01995 // Shorten in-between strings: 01996 foreach ($parts as $k => $strP) { 01997 if (($k%2)==0) { 01998 01999 // Find length of the summary part: 02000 $strLen = $GLOBALS['TSFE']->csConvObj->strlen('utf-8', $parts[$k]); 02001 $output[$k] = $parts[$k]; 02002 02003 // Possibly shorten string: 02004 if (!$k) { // First entry at all (only cropped on the frontside) 02005 if ($strLen > $postPreLgd) { 02006 $output[$k] = $divider.ereg_replace('^[^[:space:]]+[[:space:]]','',$GLOBALS['TSFE']->csConvObj->crop('utf-8',$parts[$k],-($postPreLgd-$postPreLgd_offset))); 02007 } 02008 } elseif ($summaryLgd > $summaryMax || !isset($parts[$k+1])) { // In case summary length is exceed OR if there are no more entries at all: 02009 if ($strLen > $postPreLgd) { 02010 $output[$k] = ereg_replace('[[:space:]][^[:space:]]+$','',$GLOBALS['TSFE']->csConvObj->crop('utf-8',$parts[$k],$postPreLgd-$postPreLgd_offset)).$divider; 02011 } 02012 } else { // In-between search words: 02013 if ($strLen > $postPreLgd*2) { 02014 $output[$k] = ereg_replace('[[:space:]][^[:space:]]+$','',$GLOBALS['TSFE']->csConvObj->crop('utf-8',$parts[$k],$postPreLgd-$postPreLgd_offset)). 02015 $divider. 02016 ereg_replace('^[^[:space:]]+[[:space:]]','',$GLOBALS['TSFE']->csConvObj->crop('utf-8',$parts[$k],-($postPreLgd-$postPreLgd_offset))); 02017 } 02018 } 02019 $summaryLgd+= $GLOBALS['TSFE']->csConvObj->strlen('utf-8', $output[$k]);; 02020 02021 // Protect output: 02022 $output[$k] = htmlspecialchars($output[$k]); 02023 02024 // If summary lgd is exceed, break the process: 02025 if ($summaryLgd > $summaryMax) { 02026 break; 02027 } 02028 } else { 02029 $summaryLgd+= $GLOBALS['TSFE']->csConvObj->strlen('utf-8',$strP); 02030 $output[$k] = '<strong class="tx-indexedsearch-redMarkup">'.htmlspecialchars($parts[$k]).'</strong>'; 02031 } 02032 } 02033 02034 // Return result: 02035 return implode('',$output); 02036 } 02037 02044 function makeTitle($row) { 02045 $add = ''; 02046 02047 if ($this->multiplePagesType($row['item_type'])) { 02048 $dat = unserialize($row['cHashParams']); 02049 02050 $pp = explode('-',$dat['key']); 02051 if ($pp[0]!=$pp[1]) { 02052 $add=', '.$this->pi_getLL('word_pages').' '.$dat['key']; 02053 } else $add=', '.$this->pi_getLL('word_page').' '.$pp[0]; 02054 } 02055 02056 $outputString = $GLOBALS['TSFE']->csConvObj->crop('utf-8',$row['item_title'],50,'...'); 02057 02058 return $this->utf8_to_currentCharset($outputString).$add; 02059 } 02060 02068 function makeInfo($row,$tmplArray) { 02069 $tmplArray['size'] = t3lib_div::formatSize($row['item_size']); 02070 $tmplArray['created'] = date('d-m-y',$row['item_crdate']); 02071 $tmplArray['modified'] = date('d-m-y H:i',$row['item_mtime']); 02072 02073 $pathId = $row['data_page_id']?$row['data_page_id']:$row['page_id']; 02074 $pathMP = $row['data_page_id']?$row['data_page_mp']:''; 02075 02076 $pI = parse_url($row['data_filename']); 02077 if ($pI['scheme']) { 02078 $tmplArray['path'] = '<a href="'.htmlspecialchars($row['data_filename']).'">'.htmlspecialchars($row['data_filename']).'</a>'; 02079 } else { 02080 $pathStr = htmlspecialchars($this->getPathFromPageId($pathId,$pathMP)); 02081 $tmplArray['path'] = $this->linkPage($pathId,$pathStr,array( 02082 'data_page_type' => $row['data_page_type'], 02083 'data_page_mp' => $pathMP, 02084 'sys_language_uid' => $row['sys_language_uid'], 02085 )); 02086 } 02087 02088 return $tmplArray; 02089 } 02090 02097 function getSpecialConfigForRow($row) { 02098 $pathId = $row['data_page_id'] ? $row['data_page_id'] : $row['page_id']; 02099 $pathMP = $row['data_page_id'] ? $row['data_page_mp'] : ''; 02100 02101 $rl = $this->getRootLine($pathId,$pathMP); 02102 $specConf = $this->conf['specConfs.']['0.']; 02103 if (is_array($rl)) { 02104 foreach ($rl as $dat) { 02105 if (is_array($this->conf['specConfs.'][$dat['uid'].'.'])) { 02106 $specConf = $this->conf['specConfs.'][$dat['uid'].'.']; 02107 break; 02108 } 02109 } 02110 } 02111 02112 return $specConf; 02113 } 02114 02121 function makeLanguageIndication($row) { 02122 02123 // If search result is a TYPO3 page: 02124 if ((string)$row['item_type']==='0') { 02125 02126 // If TypoScript is used to render the flag: 02127 if (is_array($this->conf['flagRendering.'])) { 02128 $this->cObj->setCurrentVal($row['sys_language_uid']); 02129 return $this->cObj->cObjGetSingle($this->conf['flagRendering'],$this->conf['flagRendering.']); 02130 } else { // ... otherwise, get flag from sys_language record: 02131 02132 // Get sys_language record 02133 $rowDat = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('*', 'sys_language', 'uid='.intval($row['sys_language_uid']).' '.$this->cObj->enableFields('sys_language')); 02134 02135 // Flag code: 02136 $flag = $rowDat[0]['flag']; 02137 if ($flag) { 02138 02139 // FIXME not all flags from typo3/gfx/flags are available in media/flags/ 02140 $file = substr(PATH_tslib,strlen(PATH_site)).'media/flags/flag_'.$flag; 02141 $imgInfo = @getimagesize(PATH_site.$file); 02142 02143 // original 02144 # $file = TYPO3_mainDir.'gfx/flags/'.$flag; 02145 # $imgInfo = @getimagesize(PATH_site.$file); 02146 02147 if (is_array($imgInfo)) { 02148 $output = '<img src="'.$file.'" '.$imgInfo[3].' title="'.htmlspecialchars($rowDat[0]['title']).'" alt="'.htmlspecialchars($rowDat[0]['title']).'" />'; 02149 return $output; 02150 } 02151 } 02152 } 02153 } 02154 return ' '; 02155 } 02156 02164 function makeAccessIndication($id) { 02165 if (is_array($this->fe_groups_required[$id]) && count($this->fe_groups_required[$id])) { 02166 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="" />'; 02167 } 02168 } 02169 02179 function linkPage($id,$str,$row=array(),$markUpSwParams=array()) { 02180 02181 // Parameters for link: 02182 $urlParameters = (array)unserialize($row['cHashParams']); 02183 02184 // Add &type and &MP variable: 02185 if ($row['data_page_type']) $urlParameters['type'] = $row['data_page_type']; 02186 if ($row['data_page_mp']) $urlParameters['MP'] = $row['data_page_mp']; 02187 if ($row['sys_language_uid']) $urlParameters['L'] = $row['sys_language_uid']; 02188 02189 // markup-GET vars: 02190 $urlParameters = array_merge($urlParameters, $markUpSwParams); 02191 02192 // 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... 02193 if (!is_array($this->domain_records[$id])) { 02194 $this->getPathFromPageId($id); 02195 } 02196 02197 // If external domain, then link to that: 02198 if (count($this->domain_records[$id])) { 02199 reset($this->domain_records[$id]); 02200 $firstDom = current($this->domain_records[$id]); 02201 $scheme = t3lib_div::getIndpEnv('TYPO3_SSL') ? 'https://' : 'http://'; 02202 02203 $addParams = ''; 02204 if (is_array($urlParameters)) { 02205 if (count($urlParameters)) { 02206 $addParams.= t3lib_div::implodeArrayForUrl('',$urlParameters); 02207 } 02208 } 02209 02210 if ($target=$this->conf['search.']['detect_sys_domain_records.']['target']) { 02211 $target = ' target="'.$target.'"'; 02212 } 02213 return '<a href="'.htmlspecialchars($scheme.$firstDom.'/index.php?id='.$id.$addParams).'"'.$target.'>'.htmlspecialchars($str).'</a>'; 02214 } else { 02215 return $this->pi_linkToPage($str,$id,$this->conf['result_link_target'],$urlParameters); 02216 } 02217 } 02218 02226 function getRootLine($id,$pathMP='') { 02227 $identStr = $id.'|'.$pathMP; 02228 02229 if (!isset($this->cache_path[$identStr])) { 02230 $this->cache_rl[$identStr] = $GLOBALS['TSFE']->sys_page->getRootLine($id,$pathMP); 02231 } 02232 return $this->cache_rl[$identStr]; 02233 } 02234 02241 function getFirstSysDomainRecordForPage($id) { 02242 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('domainName', 'sys_domain', 'pid='.intval($id).$this->cObj->enableFields('sys_domain'), '', 'sorting'); 02243 $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res); 02244 return ereg_replace('\/$','',$row['domainName']); 02245 } 02246 02254 function getPathFromPageId($id,$pathMP='') { 02255 02256 $identStr = $id.'|'.$pathMP; 02257 02258 if (!isset($this->cache_path[$identStr])) { 02259 $this->fe_groups_required[$id] = array(); 02260 $this->domain_records[$id] = array(); 02261 $rl = $this->getRootLine($id,$pathMP); 02262 $hitRoot = 0; 02263 $path = ''; 02264 if (is_array($rl) && count($rl)) { 02265 reset($rl); 02266 while(list($k,$v)=each($rl)) { 02267 // Check fe_user 02268 if ($v['fe_group'] && ($v['uid']==$id || $v['extendToSubpages'])) { 02269 $this->fe_groups_required[$id][]=$v['fe_group']; 02270 } 02271 // Check sys_domain. 02272 if ($this->conf['search.']['detect_sys_domain_records']) { 02273 $sysDName = $this->getFirstSysDomainRecordForPage($v['uid']); 02274 if ($sysDName) { 02275 $this->domain_records[$id][] = $sysDName; 02276 // Set path accordingly: 02277 $path = $sysDName.$path; 02278 break; 02279 } 02280 } 02281 02282 // Stop, if we find that the current id is the current root page. 02283 if ($v['uid']==$GLOBALS['TSFE']->config['rootLine'][0]['uid']) { 02284 break; 02285 } 02286 $path = '/'.$v['title'].$path; 02287 } 02288 } 02289 02290 $this->cache_path[$identStr] = $path; 02291 02292 if (is_array($this->conf['path_stdWrap.'])) { 02293 $this->cache_path[$identStr] = $this->cObj->stdWrap($this->cache_path[$identStr], $this->conf['path_stdWrap.']); 02294 } 02295 } 02296 02297 return $this->cache_path[$identStr]; 02298 } 02299 02306 function getMenu($id) { 02307 if ($this->conf['show.']['LxALLtypes']) { 02308 $output = Array(); 02309 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('title,uid', 'pages', 'pid='.intval($id).$this->cObj->enableFields('pages'), '', 'sorting'); 02310 while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { 02311 $output[$row['uid']] = $GLOBALS['TSFE']->sys_page->getPageOverlay($row); 02312 } 02313 return $output; 02314 } else { 02315 return $GLOBALS['TSFE']->sys_page->getMenu($id); 02316 } 02317 } 02318 02325 function multiplePagesType($item_type) { 02326 return is_object($this->external_parsers[$item_type]) && $this->external_parsers[$item_type]->isMultiplePageExtension($item_type); 02327 } 02328 02335 function utf8_to_currentCharset($str) { 02336 return $GLOBALS['TSFE']->csConv($str,'utf-8'); 02337 } 02338 02345 function &hookRequest($functionName) { 02346 global $TYPO3_CONF_VARS; 02347 02348 // Hook: menuConfig_preProcessModMenu 02349 if ($TYPO3_CONF_VARS['EXTCONF']['indexed_search']['pi1_hooks'][$functionName]) { 02350 $hookObj = &t3lib_div::getUserObj($TYPO3_CONF_VARS['EXTCONF']['indexed_search']['pi1_hooks'][$functionName]); 02351 if (method_exists ($hookObj, $functionName)) { 02352 $hookObj->pObj = &$this; 02353 return $hookObj; 02354 } 02355 } 02356 } 02357 } 02358 02359 02360 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/indexed_search/pi/class.tx_indexedsearch.php']) { 02361 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/indexed_search/pi/class.tx_indexedsearch.php']); 02362 } 02363 ?>