/src/typo3_src-4.1.2/typo3/sysext/indexed_search/pi/class.tx_indexedsearch.php

00001 <?php
00002 /***************************************************************
00003 *  Copyright notice
00004 *
00005 *  (c) 2001-2006 Kasper Skaarhoj (kasperYYYY@typo3.com)
00006 *  All rights reserved
00007 *
00008 *  This script is part of the TYPO3 project. The TYPO3 project is
00009 *  free software; you can redistribute it and/or modify
00010 *  it under the terms of the GNU General Public License as published by
00011 *  the Free Software Foundation; either version 2 of the License, or
00012 *  (at your option) any later version.
00013 *
00014 *  The GNU General Public License can be found at
00015 *  http://www.gnu.org/copyleft/gpl.html.
00016 *  A copy is found in the textfile GPL.txt and important notices to the license
00017 *  from the author is found in LICENSE.txt distributed with these scripts.
00018 *
00019 *
00020 *  This script is distributed in the hope that it will be useful,
00021 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00022 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00023 *  GNU General Public License for more details.
00024 *
00025 *  This copyright notice MUST APPEAR in all copies of the script!
00026 ***************************************************************/
00108 require_once(PATH_tslib.'class.tslib_pibase.php');
00109 require_once(PATH_tslib.'class.tslib_search.php');
00110 require_once(t3lib_extMgm::extPath('indexed_search').'class.indexer.php');
00111 
00112 
00123 class tx_indexedsearch extends tslib_pibase {
00124         var $prefixId = 'tx_indexedsearch';        // Same as class name
00125         var $scriptRelPath = 'pi/class.tx_indexedsearch.php';    // Path to this script relative to the extension dir.
00126         var $extKey = 'indexed_search';    // The extension key.
00127 
00128         var $join_pages = 0;    // See document for info about this flag...
00129         var $defaultResultNumber = 10;
00130 
00131         var $operator_translate_table = Array (         // case-sensitive. Defines the words, which will be operators between words
00132                 Array ('+' , 'AND'),
00133                 Array ('|' , 'OR'),
00134                 Array ('-' , 'AND NOT'),
00135                         // english
00136 #               Array ('AND' , 'AND'),
00137 #               Array ('OR' , 'OR'),
00138 #               Array ('NOT' , 'AND NOT'),
00139         );
00140 
00141                 // Internal variable
00142         var $wholeSiteIdList = 0;               // Root-page PIDs to search in (rl0 field where clause, see initialize() function)
00143 
00144                 // Internals:
00145         var $sWArr = array();                   // Search Words and operators
00146         var $optValues = array();               // Selector box values for search configuration form
00147         var $firstRow = Array();                // Will hold the first row in result - used to calculate relative hit-ratings.
00148 
00149         var $cache_path = array();              // Caching of page path
00150         var $cache_rl = array();                // Caching of root line data
00151         var $fe_groups_required = array();      // Required fe_groups memberships for display of a result.
00152         var $domain_records = array();          // Domain records (?)
00153         var $wSelClauses = array();             // Select clauses for individual words
00154         var $resultSections = array();          // Page tree sections for search result.
00155         var $external_parsers = array();        // External parser objects
00156         var $iconFileNameCache = array();       // Storage of icons....
00157         var $lexerObj;                          // Lexer object
00158         var $templateCode;                      // Will hold the content of $conf['templateFile']
00159         var $hiddenFieldList = 'ext, type, defOp, media, order, group, lang, desc, results';
00160 
00161 
00169         function main($content, $conf)    {
00170 
00171                         // Initialize:
00172                 $this->conf = $conf;
00173                 $this->pi_loadLL();
00174                 $this->pi_setPiVarDefaults();
00175 
00176                         // Initialize the indexer-class - just to use a few function (for making hashes)
00177                 $this->indexerObj = t3lib_div::makeInstance('tx_indexedsearch_indexer');
00178 
00179                         // Initialize:
00180                 $this->initialize();
00181 
00182                         // Do search:
00183                         // If there were any search words entered...
00184                 if (is_array($this->sWArr))     {
00185                         $content = $this->doSearch($this->sWArr);
00186                 }
00187 
00188                         // Finally compile all the content, form, messages and results:
00189                 $content = $this->makeSearchForm($this->optValues).
00190                         $this->printRules().
00191                         $content;
00192 
00193         return $this->pi_wrapInBaseClass($content);
00194     }
00195 
00201         function initialize()   {
00202                 global $TYPO3_CONF_VARS;
00203 
00204                         // Initialize external document parsers for icon display and other soft operations
00205                 if (is_array($TYPO3_CONF_VARS['EXTCONF']['indexed_search']['external_parsers']))        {
00206                         foreach ($TYPO3_CONF_VARS['EXTCONF']['indexed_search']['external_parsers'] as $extension => $_objRef)   {
00207                                 $this->external_parsers[$extension] = &t3lib_div::getUserObj($_objRef);
00208 
00209                                         // Init parser and if it returns false, unset its entry again:
00210                                 if (!$this->external_parsers[$extension]->softInit($extension)) {
00211                                         unset($this->external_parsers[$extension]);
00212                                 }
00213                         }
00214                 }
00215 
00216                         // Init lexer (used to post-processing of search words)
00217                 $lexerObjRef = $TYPO3_CONF_VARS['EXTCONF']['indexed_search']['lexer'] ?
00218                                                 $TYPO3_CONF_VARS['EXTCONF']['indexed_search']['lexer'] :
00219                                                 'EXT:indexed_search/class.lexer.php:&tx_indexedsearch_lexer';
00220                 $this->lexerObj = &t3lib_div::getUserObj($lexerObjRef);
00221 
00222                         // If "_sections" is set, this value overrides any existing value.
00223                 if ($this->piVars['_sections'])         $this->piVars['sections'] = $this->piVars['_sections'];
00224 
00225                         // If "_sections" is set, this value overrides any existing value.
00226                 if ($this->piVars['_freeIndexUid']!=='_')               $this->piVars['freeIndexUid'] = $this->piVars['_freeIndexUid'];
00227 
00228                         // Add previous search words to current
00229                 if ($this->piVars['sword_prev_include'] && $this->piVars['sword_prev']) {
00230                         $this->piVars['sword'] = trim($this->piVars['sword_prev']).' '.$this->piVars['sword'];
00231                 }
00232 
00233                 $this->piVars['results'] = t3lib_div::intInRange($this->piVars['results'],1,100000,$this->defaultResultNumber);
00234 
00235                         // Selector-box values defined here:
00236                 $this->optValues = Array(
00237                         'type' => Array(
00238                                 '0' => $this->pi_getLL('opt_type_0'),
00239                                 '1' => $this->pi_getLL('opt_type_1'),
00240                                 '2' => $this->pi_getLL('opt_type_2'),
00241                                 '3' => $this->pi_getLL('opt_type_3'),
00242                                 '10' => $this->pi_getLL('opt_type_10'),
00243                                 '20' => $this->pi_getLL('opt_type_20'),
00244                         ),
00245                         'defOp' => Array(
00246                                 '0' => $this->pi_getLL('opt_defOp_0'),
00247                                 '1' => $this->pi_getLL('opt_defOp_1'),
00248                         ),
00249                         'sections' => Array(
00250                                 '0' => $this->pi_getLL('opt_sections_0'),
00251                                 '-1' => $this->pi_getLL('opt_sections_-1'),
00252                                 '-2' => $this->pi_getLL('opt_sections_-2'),
00253                                 '-3' => $this->pi_getLL('opt_sections_-3'),
00254                                 // Here values like "rl1_" and "rl2_" + a rootlevel 1/2 id can be added to perform searches in rootlevel 1+2 specifically. The id-values can even be commaseparated. Eg. "rl1_1,2" would search for stuff inside pages on menu-level 1 which has the uid's 1 and 2.
00255                         ),
00256                         'freeIndexUid' => Array(
00257                                 '-1' => $this->pi_getLL('opt_freeIndexUid_-1'),
00258                                 '-2' => $this->pi_getLL('opt_freeIndexUid_-2'),
00259                                 '0' => $this->pi_getLL('opt_freeIndexUid_0'),
00260                         ),
00261                         'media' => Array(
00262                                 '-1' => $this->pi_getLL('opt_media_-1'),
00263                                 '0' => $this->pi_getLL('opt_media_0'),
00264                                 '-2' => $this->pi_getLL('opt_media_-2'),
00265                         ),
00266                         'order' => Array(
00267                                 'rank_flag' => $this->pi_getLL('opt_order_rank_flag'),
00268                                 'rank_freq' => $this->pi_getLL('opt_order_rank_freq'),
00269                                 'rank_first' => $this->pi_getLL('opt_order_rank_first'),
00270                                 'rank_count' => $this->pi_getLL('opt_order_rank_count'),
00271                                 'mtime' => $this->pi_getLL('opt_order_mtime'),
00272                                 'title' => $this->pi_getLL('opt_order_title'),
00273                                 'crdate' => $this->pi_getLL('opt_order_crdate'),
00274                         ),
00275                         'group' => Array (
00276                                 'sections' => $this->pi_getLL('opt_group_sections'),
00277                                 'flat' => $this->pi_getLL('opt_group_flat'),
00278                         ),
00279                         'lang' => Array (
00280                                 -1 => $this->pi_getLL('opt_lang_-1'),
00281                                 0 => $this->pi_getLL('opt_lang_0'),
00282                         ),
00283                         'desc' => Array (
00284                                 '0' => $this->pi_getLL('opt_desc_0'),
00285                                 '1' => $this->pi_getLL('opt_desc_1'),
00286                         ),
00287                         'results' => Array (
00288                                 '10' => '10',
00289                                 '20' => '20',
00290                                 '50' => '50',
00291                                 '100' => '100',
00292                         )
00293                 );
00294 
00295                         // Free Index Uid:
00296                 if ($this->conf['search.']['defaultFreeIndexUidList'])  {
00297                         $uidList = t3lib_div::intExplode(',', $this->conf['search.']['defaultFreeIndexUidList']);
00298                         $indexCfgRecords = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid,title','index_config','uid IN ('.implode(',',$uidList).')'.$this->cObj->enableFields('index_config'),'','','','uid');
00299 
00300                         foreach ($uidList as $uidValue) {
00301                                 if (is_array($indexCfgRecords[$uidValue]))      {
00302                                         $this->optValues['freeIndexUid'][$uidValue] = $indexCfgRecords[$uidValue]['title'];
00303                                 }
00304                         }
00305                 }
00306 
00307 
00308                         // Add media to search in:
00309                 if (strlen(trim($this->conf['search.']['mediaList'])))  {
00310                         $mediaList = implode(',', t3lib_div::trimExplode(',', $this->conf['search.']['mediaList'], 1));
00311                 }
00312                 foreach ($this->external_parsers as $extension => $obj) {
00313                                 // Skip unwanted extensions
00314                         if ($mediaList && !t3lib_div::inList($mediaList, $extension))   { continue; }
00315 
00316                         if ($name = $obj->searchTypeMediaTitle($extension))     {
00317                                 $this->optValues['media'][$extension] = $this->pi_getLL('opt_sections_'.$extension,$name);
00318                         }
00319                 }
00320 
00321                         // Add operators for various languages
00322                         // Converts the operators to UTF-8 and lowercase
00323                 $this->operator_translate_table[] = Array($GLOBALS['TSFE']->csConvObj->conv_case('utf-8',$GLOBALS['TSFE']->csConvObj->utf8_encode($this->pi_getLL('local_operator_AND'), $GLOBALS['TSFE']->renderCharset),'toLower') , 'AND');
00324                 $this->operator_translate_table[] = Array($GLOBALS['TSFE']->csConvObj->conv_case('utf-8',$GLOBALS['TSFE']->csConvObj->utf8_encode($this->pi_getLL('local_operator_OR'), $GLOBALS['TSFE']->renderCharset),'toLower') , 'OR');
00325                 $this->operator_translate_table[] = Array($GLOBALS['TSFE']->csConvObj->conv_case('utf-8',$GLOBALS['TSFE']->csConvObj->utf8_encode($this->pi_getLL('local_operator_NOT'), $GLOBALS['TSFE']->renderCharset),'toLower') , 'AND NOT');
00326 
00327                         // This is the id of the site root. This value may be a commalist of integer (prepared for this)
00328                 $this->wholeSiteIdList = intval($GLOBALS['TSFE']->config['rootLine'][0]['uid']);
00329 
00330                         // Creating levels for section menu:
00331                         // This selects the first and secondary menus for the "sections" selector - so we can search in sections and sub sections.
00332                 if ($this->conf['show.']['L1sections']) {
00333                         $firstLevelMenu = $this->getMenu($this->wholeSiteIdList);
00334                         while(list($kk,$mR) = each($firstLevelMenu))    {
00335                                 if ($mR['doktype']!=5)  {
00336                                         $this->optValues['sections']['rl1_'.$mR['uid']] = trim($this->pi_getLL('opt_RL1').' '.$mR['title']);
00337                                         if ($this->conf['show.']['L2sections']) {
00338                                                 $secondLevelMenu = $this->getMenu($mR['uid']);
00339                                                 while(list($kk2,$mR2) = each($secondLevelMenu)) {
00340                                                         if ($mR['doktype']!=5)  {
00341                                                                 $this->optValues['sections']['rl2_'.$mR2['uid']] = trim($this->pi_getLL('opt_RL2').' '.$mR2['title']);
00342                                                         } else unset($secondLevelMenu[$kk2]);
00343                                                 }
00344                                                 $this->optValues['sections']['rl2_'.implode(',',array_keys($secondLevelMenu))] = $this->pi_getLL('opt_RL2ALL');
00345                                         }
00346                                 } else unset($firstLevelMenu[$kk]);
00347                         }
00348                         $this->optValues['sections']['rl1_'.implode(',',array_keys($firstLevelMenu))] = $this->pi_getLL('opt_RL1ALL');
00349                 }
00350 
00351                         // Setting the list of root PIDs for the search. Notice, these page IDs MUST have a TypoScript template with root flag on them! Basically this list is used to select on the "rl0" field and page ids are registered as "rl0" only if a TypoScript template record with root flag is there.
00352                         // This happens AFTER the use of $this->wholeSiteIdList above because the above will then fetch the menu for the CURRENT site - regardless of this kind of searching here. Thus a general search will lookup in the WHOLE database while a specific section search will take the current sections...
00353                 if ($this->conf['search.']['rootPidList'])      {
00354                         $this->wholeSiteIdList = implode(',',t3lib_div::intExplode(',',$this->conf['search.']['rootPidList']));
00355                 }
00356 
00357                         // Load the template
00358                 $this->templateCode = $this->cObj->fileResource($this->conf['templateFile']);
00359 
00360                         // Add search languages:
00361                 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', 'sys_language', '1=1'.$this->cObj->enableFields('sys_language'));
00362                 while($lR = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))        {
00363                         $this->optValues['lang'][$lR['uid']] = $lR['title'];
00364                 }
00365 
00366                         // Calling hook for modification of initialized content
00367                 if ($hookObj = &$this->hookRequest('initialize_postProc'))      {
00368                         $hookObj->initialize_postProc();
00369                 }
00370 
00371                         // Default values set:
00372                         // Setting first values in optValues as default values IF there is not corresponding piVar value set already.
00373                 foreach ($this->optValues as $kk => $vv)        {
00374                         if (!isset($this->piVars[$kk])) {
00375                                 reset($vv);
00376                                 $this->piVars[$kk] = key($vv);
00377                         }
00378                 }
00379 
00380                         // Blind selectors:
00381                 if (is_array($this->conf['blind.']))    {
00382                         foreach ($this->conf['blind.'] as $kk => $vv)   {
00383                                 if (is_array($vv))      {
00384                                         foreach ($vv as $kkk => $vvv)   {
00385                                                 if (!is_array($vvv) && $vvv && is_array($this->optValues[substr($kk,0,-1)]))    {
00386                                                         unset($this->optValues[substr($kk,0,-1)][$kkk]);
00387                                                 }
00388                                         }
00389                                 } elseif ($vv) {        // If value is not set, unset the option array.
00390                                         unset($this->optValues[$kk]);
00391                                 }
00392                         }
00393                 }
00394 
00395                         // This gets the search-words into the $sWArr:
00396                 $this->sWArr = $this->getSearchWords($this->piVars['defOp']);
00397         }
00398 
00414         function getSearchWords($defOp) {
00415                         // Shorten search-word string to max 200 bytes (does NOT take multibyte charsets into account - but never mind, shortening the string here is only a run-away feature!)
00416                 $inSW = substr($this->piVars['sword'],0,200);
00417 
00418                         // Convert to UTF-8 + conv. entities (was also converted during indexing!)
00419                 $inSW = $GLOBALS['TSFE']->csConvObj->utf8_encode($inSW, $GLOBALS['TSFE']->metaCharset);
00420                 $inSW = $GLOBALS['TSFE']->csConvObj->entities_to_utf8($inSW,TRUE);
00421 
00422                 if ($hookObj = &$this->hookRequest('getSearchWords'))   {
00423                         return $hookObj->getSearchWords_splitSWords($inSW, $defOp);
00424                 } else {
00425 
00426                         if ($this->piVars['type']==20)  {
00427                                 return array(array('sword'=>trim($inSW), 'oper'=>'AND'));
00428                         } else {
00429                                 $search = t3lib_div::makeInstance('tslib_search');
00430                                 $search->default_operator = $defOp==1 ? 'OR' : 'AND';
00431                                 $search->operator_translate_table = $this->operator_translate_table;
00432                                 $search->register_and_explode_search_string($inSW);
00433 
00434                                 if (is_array($search->sword_array))     {
00435                                         return $this->procSearchWordsByLexer($search->sword_array);
00436                                 }
00437                         }
00438                 }
00439         }
00440 
00448         function procSearchWordsByLexer($SWArr) {
00449 
00450                         // Init output variable:
00451                 $newSWArr = array();
00452 
00453                         // Traverse the search word array:
00454                 foreach ($SWArr as $wordDef)    {
00455                         if (!strstr($wordDef['sword'],' '))     {       // No space in word (otherwise it might be a sentense in quotes like "there is").
00456                                         // Split the search word by lexer:
00457                                 $res = $this->lexerObj->split2Words($wordDef['sword']);
00458 
00459                                         // Traverse lexer result and add all words again:
00460                                 foreach ($res as $word) {
00461                                         $newSWArr[] = array('sword'=>$word, 'oper'=>$wordDef['oper']);
00462                                 }
00463                         } else {
00464                                 $newSWArr[] = $wordDef;
00465                         }
00466                 }
00467 
00468                         // Return result:
00469                 return $newSWArr;
00470         }
00471 
00472 
00473 
00474 
00475 
00476 
00477 
00478 
00479 
00480         /*****************************
00481          *
00482          * Main functions
00483          *
00484          *****************************/
00485 
00492         function doSearch($sWArr)       {
00493 
00494                         // Find free index uid:
00495                 $freeIndexUid = $this->piVars['freeIndexUid'];
00496                 if ($freeIndexUid==-2)  {
00497                         $freeIndexUid = $this->conf['search.']['defaultFreeIndexUidList'];
00498                 }
00499 
00500                 $indexCfgs = t3lib_div::intExplode(',',$freeIndexUid);
00501                 $accumulatedContent = '';
00502 
00503                 foreach ($indexCfgs as $freeIndexUid)   {
00504                                 // Get result rows:
00505                         $pt1 = t3lib_div::milliseconds();
00506                         if ($hookObj = &$this->hookRequest('getResultRows'))    {
00507                                 $resData = $hookObj->getResultRows($sWArr,$freeIndexUid);
00508                         } else {
00509                                 $resData = $this->getResultRows($sWArr,$freeIndexUid);
00510                         }
00511 
00512                                 // Display search results:
00513                         $pt2 = t3lib_div::milliseconds();
00514                         if ($hookObj = &$this->hookRequest('getDisplayResults'))        {
00515                                 $content = $hookObj->getDisplayResults($sWArr, $resData, $freeIndexUid);
00516                         } else {
00517                                 $content = $this->getDisplayResults($sWArr, $resData, $freeIndexUid);
00518                         }
00519 
00520                         $pt3 = t3lib_div::milliseconds();
00521 
00522                                 // Create header if we are searching more than one indexing configuration:
00523                         if (count($indexCfgs)>1)        {
00524                                 if ($freeIndexUid>0)    {
00525                                         list($indexCfgRec) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('title','index_config','uid='.intval($freeIndexUid).$this->cObj->enableFields('index_config'));
00526                                         $titleString = $indexCfgRec['title'];
00527                                 } else {
00528                                         $titleString = $this->pi_getLL('opt_freeIndexUid_header_'.$freeIndexUid);
00529                                 }
00530                                 $content = '<h1 class="tx-indexedsearch-category">'.htmlspecialchars($titleString).'</h1>'.$content;
00531                         }
00532 
00533                         $accumulatedContent.=$content;
00534                 }
00535 
00536                         // Write search statistics
00537                 $this->writeSearchStat($sWArr,$resData['count'],array($pt1,$pt2,$pt3));
00538 
00539                         // Return content:
00540                 return $accumulatedContent;
00541         }
00542 
00550         function getResultRows($sWArr,$freeIndexUid=-1) {
00551 
00552                         // Getting SQL result pointer:
00553                         $GLOBALS['TT']->push('Searching result');
00554                 $res = $this->getResultRows_SQLpointer($sWArr,$freeIndexUid);
00555                         $GLOBALS['TT']->pull();
00556 
00557                         // Organize and process result:
00558                 if ($res)       {
00559 
00560                         $count = $GLOBALS['TYPO3_DB']->sql_num_rows($res);      // Total search-result count
00561                         $pointer = t3lib_div::intInRange($this->piVars['pointer'], 0, floor($count/$this->piVars['results']));  // The pointer is set to the result page that is currently being viewed
00562 
00563                                 // Initialize result accumulation variables:
00564                         $c = 0; // Result pointer: Counts up the position in the current search-result
00565                         $grouping_phashes = array();    // Used to filter out duplicates.
00566                         $grouping_chashes = array();    // Used to filter out duplicates BASED ON cHash.
00567                         $firstRow = Array();    // Will hold the first row in result - used to calculate relative hit-ratings.
00568                         $resultRows = Array();  // Will hold the results rows for display.
00569 
00570                                 // Now, traverse result and put the rows to be displayed into an array
00571                                 // Each row should contain the fields from 'ISEC.*, IP.*' combined + artificial fields "show_resume" (boolean) and "result_number" (counter)
00572                         while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))       {
00573 
00574                                         // Set first row:
00575                                 if (!$c)        {
00576                                         $firstRow = $row;
00577                                 }
00578 
00579                                 $row['show_resume'] = $this->checkResume($row); // Tells whether we can link directly to a document or not (depends on possible right problems)
00580 
00581                                 $phashGr = !in_array($row['phash_grouping'], $grouping_phashes);
00582                                 $chashGr = !in_array($row['contentHash'].'.'.$row['data_page_id'], $grouping_chashes);
00583                                 if ($phashGr && $chashGr)       {
00584                                         if ($row['show_resume'] || $this->conf['show.']['forbiddenRecords'])    {       // Only if the resume may be shown are we going to filter out duplicates...
00585                                                 if (!$this->multiplePagesType($row['item_type']))       {       // Only on documents which are not multiple pages documents
00586                                                         $grouping_phashes[] = $row['phash_grouping'];
00587                                                 }
00588                                                 $grouping_chashes[] = $row['contentHash'].'.'.$row['data_page_id'];
00589 
00590                                                 $c++;   // Increase the result pointer
00591 
00592                                                         // All rows for display is put into resultRows[]
00593                                                 if ($c > $pointer * $this->piVars['results'])   {
00594                                                         $row['result_number'] = $c;
00595                                                         $resultRows[] = $row;
00596                                                                 // This may lead to a problem: If the result check is not stopped here, the search will take longer. However the result counter will not filter out grouped cHashes/pHashes that were not processed yet.
00597                                                         if (($c+1) > ($pointer+1)*$this->piVars['results'])     break;
00598                                                 }
00599                                         } else {
00600                                                 $count--;       // Skip this row if the user cannot view it (missing permission)
00601                                         }
00602                                 } else {
00603                                         $count--;       // For each time a phash_grouping document is found (which is thus not displayed) the search-result count is reduced, so that it matches the number of rows displayed.
00604                                 }
00605                         }
00606 
00607                         return array(
00608                                                 'resultRows' => $resultRows,
00609                                                 'firstRow' => $firstRow,
00610                                                 'count' => $count
00611                                         );
00612                 } else {        // No results found:
00613                         return FALSE;
00614                 }
00615         }
00616 
00624         function getResultRows_SQLpointer($sWArr,$freeIndexUid=-1)      {
00625                                 // This SEARCHES for the searchwords in $sWArr AND returns a COMPLETE list of phash-integers of the matches.
00626                 $list = $this->getPhashList($sWArr);
00627 
00628                         // Perform SQL Search / collection of result rows array:
00629                 if ($list)      {
00630                                 // Do the search:
00631                         $GLOBALS['TT']->push('execFinalQuery');
00632                         $res = $this->execFinalQuery($list,$freeIndexUid);
00633                         $GLOBALS['TT']->pull();
00634                         return $res;
00635                 } else {
00636                         return FALSE;
00637                 }
00638         }
00639 
00648         function getDisplayResults($sWArr, $resData, $freeIndexUid=-1)  {
00649                         // Perform display of result rows array:
00650                 if ($resData)   {
00651                         $GLOBALS['TT']->push('Display Final result');
00652 
00653                                 // Set first selected row (for calculation of ranking later)
00654                         $this->firstRow = $resData['firstRow'];
00655 
00656                                 // Result display here:
00657                         $rowcontent = '';
00658                         $rowcontent.= $this->compileResult($resData['resultRows'], $freeIndexUid);
00659 
00660                                 // Browsing box:
00661                         if ($resData['count'])  {
00662                                 $this->internal['res_count'] = $resData['count'];
00663                                 $this->internal['results_at_a_time'] = $this->piVars['results'];
00664                                 $this->internal['maxPages'] = t3lib_div::intInRange($this->conf['search.']['page_links'],1,100,10);
00665                                 $addString = ($resData['count']&&$this->piVars['group']=='sections'&&$freeIndexUid<=0 ? ' '.sprintf($this->pi_getLL(count($this->resultSections)>1?'inNsections':'inNsection'),count($this->resultSections)):'');
00666                                 $browseBox1 = $this->pi_list_browseresults(1,$addString,$this->printResultSectionLinks(),$freeIndexUid);
00667                                 $browseBox2 = $this->pi_list_browseresults(0,'','',$freeIndexUid);
00668                         }
00669 
00670                                 // Browsing nav, bottom.
00671                         if ($resData['count'])  {
00672                                 $content = $browseBox1.$rowcontent.$browseBox2;
00673                         } else {
00674                                 $content = '<p'.$this->pi_classParam('noresults').'>'.$this->pi_getLL('noResults','',1).'</p>';
00675                         }
00676 
00677                         $GLOBALS['TT']->pull();
00678                 } else {
00679                         $content.='<p'.$this->pi_classParam('noresults').'>'.$this->pi_getLL('noResults','',1).'</p>';
00680                 }
00681 
00682                         // Print a message telling which words we searched for, and in which sections etc.
00683                 $what = $this->tellUsWhatIsSeachedFor($sWArr).
00684                                 (substr($this->piVars['sections'],0,2)=='rl'?' '.$this->pi_getLL('inSection','',1).' "'.substr($this->getPathFromPageId(substr($this->piVars['sections'],4)),1).'"':'');
00685                 $what = '<div'.$this->pi_classParam('whatis').'>'.$this->cObj->stdWrap($what, $this->conf['whatis_stdWrap.']).'</div>';
00686                 $content = $what.$content;
00687 
00688                         // Return content:
00689                 return $content;
00690         }
00691 
00700         function compileResult($resultRows, $freeIndexUid=-1)   {
00701                 $content = '';
00702 
00703                         // Transfer result rows to new variable, performing some mapping of sub-results etc.
00704                 $newResultRows = array();
00705                 foreach ($resultRows as $row)   {
00706                         $id = md5($row['phash_grouping']);
00707                         if (is_array($newResultRows[$id]))      {
00708                                 if (!$newResultRows[$id]['show_resume'] && $row['show_resume']) {       // swapping:
00709 
00710                                                 // Remove old
00711                                         $subrows = $newResultRows[$id]['_sub'];
00712                                         unset($newResultRows[$id]['_sub']);
00713                                         $subrows[] = $newResultRows[$id];
00714 
00715                                                 // Insert new:
00716                                         $newResultRows[$id] = $row;
00717                                         $newResultRows[$id]['_sub'] = $subrows;
00718                                 } else $newResultRows[$id]['_sub'][] = $row;
00719                         } else {
00720                                 $newResultRows[$id] = $row;
00721                         }
00722                 }
00723                 $resultRows = $newResultRows;
00724                 $this->resultSections = array();
00725 
00726                 if ($freeIndexUid<=0)   {
00727                         switch($this->piVars['group'])  {
00728                                 case 'sections':
00729 
00730                                         $rl2flag = substr($this->piVars['sections'],0,2)=='rl';
00731                                         $sections = array();
00732                                         foreach ($resultRows as $row)   {
00733                                                 $id = $row['rl0'].'-'.$row['rl1'].($rl2flag?'-'.$row['rl2']:'');
00734                                                 $sections[$id][] = $row;
00735                                         }
00736 
00737                                         $this->resultSections = array();
00738 
00739                                         foreach ($sections as $id => $resultRows)       {
00740                                                 $rlParts = explode('-',$id);
00741                                                 $theId = $rlParts[2] ? $rlParts[2] : ($rlParts[1]?$rlParts[1]:$rlParts[0]);
00742                                                 $theRLid = $rlParts[2] ? 'rl2_'.$rlParts[2]:($rlParts[1]?'rl1_'.$rlParts[1]:'0');
00743 
00744                                                 $sectionName = $this->getPathFromPageId($theId);
00745                                                 if ($sectionName{0} == '/') $sectionName = substr($sectionName,1);
00746 
00747                                                 if (!trim($sectionName))        {
00748                                                         $sectionTitleLinked = $this->pi_getLL('unnamedSection','',1).':';
00749                                                 } else {
00750                                                         $onclick = 'document.'.$this->prefixId.'[\''.$this->prefixId.'[_sections]\'].value=\''.$theRLid.'\';document.'.$this->prefixId.'.submit();return false;';
00751                                                         $sectionTitleLinked = '<a href="#" onclick="'.htmlspecialchars($onclick).'">'.htmlspecialchars($sectionName).':</a>';
00752                                                 }
00753                                                 $this->resultSections[$id] = array($sectionName,count($resultRows));
00754 
00755                                                         // Add content header:
00756                                                 $content.= $this->makeSectionHeader($id,$sectionTitleLinked,count($resultRows));
00757 
00758                                                         // Render result rows:
00759                                                 foreach ($resultRows as $row)   {
00760                                                         $content.= $this->printResultRow($row);
00761                                                 }
00762                                         }
00763                                 break;
00764                                 default:        // flat:
00765                                         foreach ($resultRows as $row)   {
00766                                                 $content.= $this->printResultRow($row);
00767                                         }
00768                                 break;
00769                         }
00770                 } else {
00771                         foreach ($resultRows as $row)   {
00772                                 $content.= $this->printResultRow($row);
00773                         }
00774                 }
00775 
00776                 return '<div'.$this->pi_classParam('res').'>'.$content.'</div>';
00777         }
00778 
00779 
00780 
00781 
00782 
00783 
00784 
00785 
00786 
00787 
00788 
00789         /***********************************
00790          *
00791          *      Searching functions (SQL)
00792          *
00793          ***********************************/
00794 
00802         function getPhashList($sWArr)   {
00803 
00804                         // Initialize variables:
00805                 $c=0;
00806                 $totalHashList = array();       // This array accumulates the phash-values
00807                 $this->wSelClauses = array();
00808 
00809                         // Traverse searchwords; for each, select all phash integers and merge/diff/intersect them with previous word (based on operator)
00810                 foreach ($sWArr as $k => $v)    {
00811                                 // Making the query for a single search word based on the search-type
00812                         $sWord = $v['sword'];   // $GLOBALS['TSFE']->csConvObj->conv_case('utf-8',$v['sword'],'toLower');       // lower-case all of them...
00813                         $theType = (string)$this->piVars['type'];
00814                         if (strstr($sWord,' ')) $theType = 20;  // If there are spaces in the search-word, make a full text search instead.
00815 
00816                         $GLOBALS['TT']->push('SearchWord "'.$sWord.'" - $theType='.$theType);
00817 
00818                         $res = '';
00819                         $wSel='';
00820 
00821                                 // Perform search for word:
00822                         switch($theType)        {
00823                                 case '1':       // Part of word
00824                                         $wSel = "IW.baseword LIKE '%".$GLOBALS['TYPO3_DB']->quoteStr($sWord, 'index_words')."%'";
00825                                         $res = $this->execPHashListQuery($wSel,' AND is_stopword=0');
00826                                 break;
00827                                 case '2':       // First part of word
00828                                         $wSel = "IW.baseword LIKE '".$GLOBALS['TYPO3_DB']->quoteStr($sWord, 'index_words')."%'";
00829                                         $res = $this->execPHashListQuery($wSel,' AND is_stopword=0');
00830                                 break;
00831                                 case '3':       // Last part of word
00832                                         $wSel = "IW.baseword LIKE '%".$GLOBALS['TYPO3_DB']->quoteStr($sWord, 'index_words')."'";
00833                                         $res = $this->execPHashListQuery($wSel,' AND is_stopword=0');
00834                                 break;
00835                                 case '10':      // Sounds like
00836                                         $wSel = 'IW.metaphone = '.$this->indexerObj->metaphone($sWord);
00837                                         $res = $this->execPHashListQuery($wSel,' AND is_stopword=0');
00838                                 break;
00839                                 case '20':      // Sentence
00840                                         $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
00841                                                                 'ISEC.phash',
00842                                                                 'index_section ISEC, index_fulltext IFT',
00843                                                                 'IFT.fulltextdata LIKE \'%'.$GLOBALS['TYPO3_DB']->quoteStr($sWord, 'index_fulltext').'%\' AND
00844                                                                         ISEC.phash = IFT.phash
00845                                                                         '.$this->sectionTableWhere(),
00846                                                                 'ISEC.phash'
00847                                                         );
00848                                         $wSel = '1=1';
00849 
00850                                         if ($this->piVars['type']==20)  $this->piVars['order'] = 'mtime';               // If there is a fulltext search for a sentence there is a likeliness that sorting cannot be done by the rankings from the rel-table (because no relations will exist for the sentence in the word-table). So therefore mtime is used instaed. It is not required, but otherwise some hits may be left out.
00851                                 break;
00852                                 default:        // Distinct word
00853                                         $wSel = 'IW.wid = '.$hash = $this->indexerObj->md5inthash($sWord);
00854                                         $res = $this->execPHashListQuery($wSel,' AND is_stopword=0');
00855                                 break;
00856                         }
00857 
00858                                 // Accumulate the word-select clauses
00859                         $this->wSelClauses[] = $wSel;
00860 
00861                                 // If there was a query to do, then select all phash-integers which resulted from this.
00862                         if ($res)       {
00863 
00864                                         // Get phash list by searching for it:
00865                                 $phashList = array();
00866                                 while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))       {
00867                                         $phashList[] = $row['phash'];
00868                                 }
00869                                 $GLOBALS['TYPO3_DB']->sql_free_result($res);
00870 
00871                                         // Here the phash list are merged with the existing result based on whether we are dealing with OR, NOT or AND operations.
00872                                 if ($c) {
00873                                         switch($v['oper'])      {
00874                                                 case 'OR':
00875                                                         $totalHashList = array_unique(array_merge($phashList,$totalHashList));
00876                                                 break;
00877                                                 case 'AND NOT':
00878                                                         $totalHashList = array_diff($totalHashList,$phashList);
00879                                                 break;
00880                                                 default:        // AND...
00881                                                         $totalHashList = array_intersect($totalHashList,$phashList);
00882                                                 break;
00883                                         }
00884                                 } else {
00885                                         $totalHashList = $phashList;    // First search
00886                                 }
00887                         }
00888 
00889                         $GLOBALS['TT']->pull();
00890                         $c++;
00891                 }
00892 
00893                 return implode(',',$totalHashList);
00894         }
00895 
00903         function execPHashListQuery($wordSel,$plusQ='') {
00904                 return $GLOBALS['TYPO3_DB']->exec_SELECTquery(
00905                                         'IR.phash',
00906                                         'index_words IW,
00907                                                 index_rel IR,
00908                                                 index_section ISEC',
00909                                         $wordSel.'
00910                                                 AND IW.wid=IR.wid
00911                                                 AND ISEC.phash = IR.phash
00912                                                 '.$this->sectionTableWhere().'
00913                                                 '.$plusQ,
00914                                         'IR.phash'
00915                                 );
00916         }
00917 
00923         function sectionTableWhere()    {
00924                 $out = $this->wholeSiteIdList<0 ? '' : 'AND ISEC.rl0 IN ('.$this->wholeSiteIdList.')';
00925 
00926                 $match = '';
00927                 if (substr($this->piVars['sections'],0,4)=='rl1_')      {
00928                         $list = implode(',',t3lib_div::intExplode(',',substr($this->piVars['sections'],4)));
00929                         $out.= 'AND ISEC.rl1 IN ('.$list.')';
00930                         $match = TRUE;
00931                 } elseif (substr($this->piVars['sections'],0,4)=='rl2_')        {
00932                         $list = implode(',',t3lib_div::intExplode(',',substr($this->piVars['sections'],4)));
00933                         $out.= 'AND ISEC.rl2 IN ('.$list.')';
00934                         $match = TRUE;
00935                 } elseif (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['addRootLineFields']))      {
00936                                 // Traversing user configured fields to see if any of those are used to limit search to a section:
00937                         foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['addRootLineFields'] as $fieldName => $rootLineLevel) {
00938                                 if (substr($this->piVars['sections'],0,strlen($fieldName)+1)==$fieldName.'_')   {
00939                                         $list = implode(',',t3lib_div::intExplode(',',substr($this->piVars['sections'],strlen($fieldName)+1)));
00940                                         $out.= 'AND ISEC.'.$fieldName.' IN ('.$list.')';
00941                                         $match = TRUE;
00942                                         break;
00943                                 }
00944                         }
00945                 }
00946 
00947                         // If no match above, test the static types:
00948                 if (!$match)    {
00949                         switch((string)$this->piVars['sections'])       {
00950                                 case '-1':              // '-1' => 'Only this page',
00951                                         $out.= ' AND ISEC.page_id='.$GLOBALS['TSFE']->id;
00952                                 break;
00953                                 case '-2':              // '-2' => 'Top + level 1',
00954                                         $out.= ' AND ISEC.rl2=0';
00955                                 break;
00956                                 case '-3':              // '-3' => 'Level 2 and out',
00957                                         $out.= ' AND ISEC.rl2>0';
00958                                 break;
00959                         }
00960                 }
00961 
00962                 return $out;
00963         }
00964 
00970         function mediaTypeWhere()       {
00971 
00972                 switch((string)$this->piVars['media'])  {
00973                         case '0':               // '0' => 'Kun TYPO3 sider',
00974                                 $out = 'AND IP.item_type='.$GLOBALS['TYPO3_DB']->fullQuoteStr('0', 'index_phash');;
00975                         break;
00976                         case '-2':              // All external documents
00977                                 $out = 'AND IP.item_type!='.$GLOBALS['TYPO3_DB']->fullQuoteStr('0', 'index_phash');;
00978                         break;
00979                         case '-1':      // All content
00980                                 $out='';
00981                         break;
00982                         default:
00983                                 $out = 'AND IP.item_type='.$GLOBALS['TYPO3_DB']->fullQuoteStr($this->piVars['media'], 'index_phash');
00984                         break;
00985                 }
00986 
00987                 return $out;
00988         }
00989 
00995         function languageWhere()        {
00996                 if ($this->piVars['lang']>=0)   {       // -1 is the same as ALL language.
00997                         return 'AND IP.sys_language_uid='.intval($this->piVars['lang']);
00998                 }
00999         }
01000 
01007         function freeIndexUidWhere($freeIndexUid)       {
01008 
01009                 if ($freeIndexUid>=0)   {
01010 
01011                                 // First, look if the freeIndexUid is a meta configuration:
01012                         list($indexCfgRec) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('indexcfgs','index_config','type=5 AND uid='.intval($freeIndexUid).$this->cObj->enableFields('index_config'));
01013                         if (is_array($indexCfgRec))     {
01014                                 $refs = t3lib_div::trimExplode(',',$indexCfgRec['indexcfgs']);
01015                                 $list = array(-99);     // Default value to protect against empty array.
01016                                 foreach ($refs as $ref) {
01017                                         list($table,$uid) = t3lib_div::revExplode('_',$ref,2);
01018                                         switch ($table) {
01019                                                 case 'index_config':
01020                                                         list($idxRec) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid','index_config','uid='.intval($uid).$this->cObj->enableFields('index_config'));
01021                                                         if ($idxRec)    $list[] = $uid;
01022                                                 break;
01023                                                 case 'pages':
01024                                                         $indexCfgRecordsFromPid = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid','index_config','pid='.intval($uid).$this->cObj->enableFields('index_config'));
01025                                                         foreach ($indexCfgRecordsFromPid as $idxRec)    {
01026                                                                 $list[] = $idxRec['uid'];
01027                                                         }
01028                                                 break;
01029                                         }
01030                                 }
01031 
01032                                 $list = array_unique($list);
01033                         } else {
01034                                 $list = array(intval($freeIndexUid));
01035                         }
01036 
01037                         return ' AND IP.freeIndexUid IN ('.implode(',',$list).')';
01038                 }
01039         }
01040 
01048         function execFinalQuery($list,$freeIndexUid=-1) {
01049 
01050                         // Setting up methods of filtering results based on page types, access, etc.
01051                 $page_join = '';
01052                 $page_where = '';
01053 
01054                         // Indexing configuration clause:
01055                 $freeIndexUidClause = $this->freeIndexUidWhere($freeIndexUid);
01056 
01057                         // Calling hook for alternative creation of page ID list
01058                 if ($hookObj = &$this->hookRequest('execFinalQuery_idList'))    {
01059                         $page_where = $hookObj->execFinalQuery_idList($list);
01060                 } elseif ($this->join_pages)    {       // Alternative to getting all page ids by ->getTreeList() where "excludeSubpages" is NOT respected.
01061                         $page_join = ',
01062                                 pages';
01063                         $page_where = 'pages.uid = ISEC.page_id
01064                                 '.$this->cObj->enableFields('pages').'
01065                                 AND pages.no_search=0
01066                                 AND pages.doktype<200
01067                         ';
01068                 } elseif ($this->wholeSiteIdList>=0) {  // Collecting all pages IDs in which to search; filtering out ALL pages that are not accessible due to enableFields. Does NOT look for "no_search" field!
01069                         $siteIdNumbers = t3lib_div::intExplode(',',$this->wholeSiteIdList);
01070                         $id_list=array();
01071                         while(list(,$rootId)=each($siteIdNumbers))      {
01072                                 $id_list[] = $this->cObj->getTreeList($rootId,9999,0,0,'','').$rootId;
01073                         }
01074                         $page_where = 'ISEC.page_id IN ('.implode(',',$id_list).')';
01075                 } else {        // Disable everything... (select all)
01076                         $page_where = ' 1=1 ';
01077                 }
01078 
01079 
01080                         // If any of the ranking sortings are selected, we must make a join with the word/rel-table again, because we need to calculate ranking based on all search-words found.
01081                 if (substr($this->piVars['order'],0,5)=='rank_')        {
01082                                 /*
01083                                          OK there were some fancy calculations promoted by Graeme Merrall:
01084 
01085                                         "However, regarding relevance you probably want to look at something like
01086                                         Salton's formula which is a good easy way to measure relevance.
01087                                         Oracle Intermedia uses this and it's pretty simple:
01088                                         Score can be between 0 and 100, but the top-scoring document in the query
01089                                         will not necessarily have a score of 100 -- scoring is relative, not
01090                                         absolute. This means that scores are not comparable across indexes, or even
01091                                         across different queries on the same index. Score for each document is
01092                                         computed using the standard Salton formula:
01093 
01094                                             3f(1+log(N/n))
01095 
01096                                         Where f is the frequency of the search term in the document, N is the total
01097                                         number of rows in the table, and n is the number of rows which contain the
01098                                         search term. This is converted into an integer in the range 0 - 100.
01099 
01100                                         There's a good doc on it at
01101                                         http://ls6-www.informatik.uni-dortmund.de/bib/fulltext/ir/Pfeifer:97/
01102                                         although it may be a little complex for what you require so just pick the
01103                                         relevant parts out.
01104                                         "
01105 
01106                                         However I chose not to go with this for several reasons.
01107                                         I do not claim that my ways of calculating importance here is the best.
01108                                         ANY (better) suggestion for ranking calculation is accepted! (as long as they are shipped with tested code in exchange for this.)
01109                                 */
01110 
01111                         switch($this->piVars['order'])  {
01112                                 case 'rank_flag':       // This gives priority to word-position (max-value) so that words in title, keywords, description counts more than in content.
01113                                                                         // The ordering is refined with the frequency sum as well.
01114                                         $grsel = 'MAX(IR.flags) AS order_val1, SUM(IR.freq) AS order_val2';
01115                                         $orderBy = 'order_val1'.$this->isDescending().',order_val2'.$this->isDescending();
01116                                 break;
01117                                 case 'rank_first':      // Results in average position of search words on page. Must be inversely sorted (low numbers are closer to top)
01118                                         $grsel = 'AVG(IR.first) AS order_val';
01119                                         $orderBy = 'order_val'.$this->isDescending(1);
01120                                 break;
01121                                 case 'rank_count':      // Number of words found
01122                                         $grsel = 'SUM(IR.count) AS order_val';
01123                                         $orderBy = 'order_val'.$this->isDescending();
01124                                 break;
01125                                 default:        // Frequency sum. I'm not sure if this is the best way to do it (make a sum...). Or should it be the average?
01126                                         $grsel = 'SUM(IR.freq) AS order_val';
01127                                         $orderBy = 'order_val'.$this->isDescending();
01128                                 break;
01129                         }
01130 
01131                                 // So, words are imploded into an OR statement (no "sentence search" should be done here - may deselect results)
01132                         $wordSel='('.implode(' OR ',$this->wSelClauses).') AND ';
01133 
01134                         return $GLOBALS['TYPO3_DB']->exec_SELECTquery(
01135                                                 'ISEC.*, IP.*, '
01136                                                 .$grsel,
01137                                                 'index_words IW,
01138                                                         index_rel IR,
01139                                                         index_section ISEC,
01140                                                         index_phash IP'.
01141                                                         $page_join,
01142                                                 $wordSel.'
01143                                                         IP.phash IN ('.$list.') '.
01144                                                         $this->mediaTypeWhere().' '.
01145                                                         $this->languageWhere().
01146                                                         $freeIndexUidClause.'
01147                                                         AND IW.wid=IR.wid
01148                                                         AND ISEC.phash = IR.phash
01149                                                         AND IP.phash = IR.phash
01150                                                         AND     '.$page_where,
01151                                                 'IP.phash,ISEC.phash,ISEC.phash_t3,ISEC.rl0,ISEC.rl1,ISEC.rl2 ,ISEC.page_id,ISEC.uniqid,IP.phash_grouping,IP.data_filename ,IP.data_page_id ,IP.data_page_reg1,IP.data_page_type,IP.data_page_mp,IP.gr_list,IP.item_type,IP.item_title,IP.item_description,IP.item_mtime,IP.tstamp,IP.item_size,IP.contentHash,IP.crdate,IP.parsetime,IP.sys_language_uid,IP.item_crdate,IP.cHashParams,IP.externalUrl,IP.recordUid,IP.freeIndexUid,IP.freeIndexSetId',
01152                                                 $orderBy
01153                                         );
01154                 } else {        // Otherwise, if sorting are done with the pages table or other fields, there is no need for joining with the rel/word tables:
01155 
01156                         $orderBy = '';
01157                         switch((string)$this->piVars['order'])  {
01158                                 case 'title':
01159                                         $orderBy = 'IP.item_title'.$this->isDescending();
01160                                 break;
01161                                 case 'crdate':
01162                                         $orderBy = 'IP.item_crdate'.$this->isDescending();
01163                                 break;
01164                                 case 'mtime':
01165                                         $orderBy = 'IP.item_mtime'.$this->isDescending();
01166                                 break;
01167                         }
01168 
01169                         return $GLOBALS['TYPO3_DB']->exec_SELECTquery(
01170                                                 'ISEC.*, IP.*',
01171                                                 'index_phash IP,index_section ISEC'.$page_join,
01172                                                 'IP.phash IN ('.$list.') '.
01173                                                         $this->mediaTypeWhere().' '.
01174                                                         $this->languageWhere().
01175                                                         $freeIndexUidClause.'
01176                                                         AND IP.phash = ISEC.phash
01177                                                         AND '.$page_where,
01178                                                 'IP.phash,ISEC.phash,ISEC.phash_t3,ISEC.rl0,ISEC.rl1,ISEC.rl2 ,ISEC.page_id,ISEC.uniqid,IP.phash_grouping,IP.data_filename ,IP.data_page_id ,IP.data_page_reg1,IP.data_page_type,IP.data_page_mp,IP.gr_list,IP.item_type,IP.item_title,IP.item_description,IP.item_mtime,IP.tstamp,IP.item_size,IP.contentHash,IP.crdate,IP.parsetime,IP.sys_language_uid,IP.item_crdate,IP.cHashParams,IP.externalUrl,IP.recordUid,IP.freeIndexUid,IP.freeIndexSetId',
01179                                                 $orderBy
01180                                         );
01181                 }
01182         }
01183 
01191         function checkResume($row)      {
01192 
01193                         // If the record is indexed by an indexing configuration, just show it.
01194                         // At least this is needed for external URLs and files.
01195                         // For records we might need to extend this - for instance block display if record is access restricted.
01196                 if ($row['freeIndexUid'])       {
01197                         return TRUE;
01198                 }
01199 
01200                         // Evaluate regularly indexed pages based on item_type:
01201                 if ($row['item_type'])  {       // External media:
01202                                 // For external media we will check the access of the parent page on which the media was linked from.
01203                                 // "phash_t3" is the phash of the parent TYPO3 page row which initiated the indexing of the documents in this section.
01204                                 // So, selecting for the grlist records belonging to the parent phash-row where the current users gr_list exists will help us to know.
01205                                 // If this is NOT found, there is still a theoretical possibility that another user accessible page would display a link, so maybe the resume of such a document here may be unjustified hidden. But better safe than sorry.
01206                         $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('phash', 'index_grlist', 'phash='.intval($row['phash_t3']).' AND gr_list='.$GLOBALS['TYPO3_DB']->fullQuoteStr($GLOBALS['TSFE']->gr_list, 'index_grlist'));
01207                         if ($GLOBALS['TYPO3_DB']->sql_num_rows($res))   {
01208                                 #debug("Look up for external media '".$row['data_filename']."': phash:".$row['phash_t3'].' YES - ('.$GLOBALS['TSFE']->gr_list.")!",1);
01209                                 return TRUE;
01210                         } else {
01211                                 #debug("Look up for external media '".$row['data_filename']."': phash:".$row['phash_t3'].' NO - ('.$GLOBALS['TSFE']->gr_list.")!",1);
01212                                 return FALSE;
01213                         }
01214                 } else {        // Ordinary TYPO3 pages:
01215                         if (strcmp($row['gr_list'],$GLOBALS['TSFE']->gr_list))  {
01216                                         // Selecting for the grlist records belonging to the phash-row where the current users gr_list exists. If it is found it is proof that this user has direct access to the phash-rows content although he did not himself initiate the indexing...
01217                                 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('phash', 'index_grlist', 'phash='.intval($row['phash']).' AND gr_list='.$GLOBALS['TYPO3_DB']->fullQuoteStr($GLOBALS['TSFE']->gr_list, 'index_grlist'));
01218                                 if ($GLOBALS['TYPO3_DB']->sql_num_rows($res))   {
01219                                         #debug('Checking on it ...'.$row['item_title'].'/'.$row['phash'].' - YES ('.$GLOBALS['TSFE']->gr_list.")",1);
01220                                         return TRUE;
01221                                 } else {
01222                                         #debug('Checking on it ...'.$row['item_title'].'/'.$row['phash']." - NOPE",1);
01223                                         return FALSE;
01224                                 }
01225                         } else {
01226                                         #debug('Resume can be shown, because the document was in fact indexed by this combination of groups!'.$GLOBALS['TSFE']->gr_list.' - '.$row['item_title'].'/'.$row['phash'],1);
01227                                 return TRUE;
01228                         }