Documentation TYPO3 par Ameos

class.tx_indexedsearch.php

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).'&nbsp;</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').' />&nbsp;&nbsp;<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).'&nbsp;</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).'&nbsp;</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).'&nbsp;</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).'&nbsp;</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']).'&nbsp;'.$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).'&nbsp;</p></td>
01224                                         <td><p>'.$this->renderSelectBox($this->prefixId.'[group]',$this->piVars['group'],$optValues['group']).
01225                                         (!$this->conf['blind.']['extResume'] ? '&nbsp; &nbsp;
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 '&nbsp;';
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('&nbsp;',' ',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 '&nbsp;';
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 ?>


Généré par Les experts TYPO3 avec  doxygen 1.4.6