PageRenderTime 60ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/search/classes/general/search.php

https://gitlab.com/alexprowars/bitrix
PHP | 1694 lines | 1661 code | 27 blank | 6 comment | 26 complexity | 8c413c375c803816938d05406a4f982d MD5 | raw file
  1. <?php
  2. IncludeModuleLangFile(__FILE__);
  3. if (!defined("START_EXEC_TIME"))
  4. define("START_EXEC_TIME", getmicrotime());
  5. class CAllSearch extends CDBResult
  6. {
  7. var $Query; //Query parset
  8. var $Statistic; //Search statistic
  9. var $strQueryText = false; //q
  10. var $strTagsText = false; //tags
  11. var $strSqlWhere = ""; //additional sql filter
  12. var $strTags = ""; //string of tags in double quotes separated by commas
  13. var $errorno = 0;
  14. var $error = false;
  15. var $arParams = array();
  16. var $url_add_params = array(); //additional url params (OnSearch event)
  17. var $tf_hwm = 0;
  18. var $tf_hwm_site_id = "";
  19. var $_opt_ERROR_ON_EMPTY_STEM = false;
  20. var $_opt_NO_WORD_LOGIC = false;
  21. var $offset = false;
  22. var $limit = false;
  23. var $bUseRatingSort = false;
  24. var $flagsUseRatingSort = 0;
  25. /** @var CSearchFormatter */
  26. var $formatter = null;
  27. function __construct($strQuery = false, $SITE_ID = false, $MODULE_ID = false, $ITEM_ID = false, $PARAM1 = false, $PARAM2 = false, $aSort = array(), $aParamsEx = array(), $bTagsCloud = false)
  28. {
  29. $this->limit = (int)COption::GetOptionInt("search", "max_result_size");
  30. if ($this->limit < 1)
  31. $this->limit = 500;
  32. $this->CSearch($strQuery, $SITE_ID, $MODULE_ID, $ITEM_ID, $PARAM1, $PARAM2, $aSort, $aParamsEx, $bTagsCloud);
  33. }
  34. function CSearch($strQuery = false, $LID = false, $MODULE_ID = false, $ITEM_ID = false, $PARAM1 = false, $PARAM2 = false, $aSort = array(), $aParamsEx = array(), $bTagsCloud = false)
  35. {
  36. if ($strQuery === false)
  37. return $this;
  38. $arParams["QUERY"] = $strQuery;
  39. $arParams["SITE_ID"] = $LID;
  40. $arParams["MODULE_ID"] = $MODULE_ID;
  41. $arParams["ITEM_ID"] = $ITEM_ID;
  42. $arParams["PARAM1"] = $PARAM1;
  43. $arParams["PARAM2"] = $PARAM2;
  44. $this->Search($arParams, $aSort, $aParamsEx, $bTagsCloud);
  45. }
  46. //combination ($MODULE_ID, $PARAM1, $PARAM2, $PARAM3) is used to narrow search
  47. //returns recordset with search results
  48. function Search($arParams, $aSort = array(), $aParamsEx = array(), $bTagsCloud = false)
  49. {
  50. $DB = CDatabase::GetModuleConnection('search');
  51. if (!is_array($arParams))
  52. $arParams = array("QUERY" => $arParams);
  53. if (!is_set($arParams, "SITE_ID") && is_set($arParams, "LID"))
  54. {
  55. $arParams["SITE_ID"] = $arParams["LID"];
  56. unset($arParams["LID"]);
  57. }
  58. if (array_key_exists("TAGS", $arParams))
  59. {
  60. $this->strTagsText = $arParams["TAGS"];
  61. $arTags = explode(",", $arParams["TAGS"]);
  62. foreach ($arTags as $i => $strTag)
  63. {
  64. $strTag = trim($strTag);
  65. if($strTag <> '')
  66. {
  67. $arTags[$i] = str_replace("\"", "\\\"", $strTag);
  68. }
  69. else
  70. {
  71. unset($arTags[$i]);
  72. }
  73. }
  74. if (count($arTags))
  75. $arParams["TAGS"] = '"'.implode('","', $arTags).'"';
  76. else
  77. unset($arParams["TAGS"]);
  78. }
  79. $this->strQueryText = $strQuery = trim($arParams["QUERY"]);
  80. $this->strTags = $strTags = $arParams["TAGS"];
  81. if (($strQuery == '') && ($strTags <> ''))
  82. {
  83. $strQuery = $strTags;
  84. $bTagsSearch = true;
  85. }
  86. else
  87. {
  88. if($strTags <> '')
  89. {
  90. $strQuery .= " ".$strTags;
  91. }
  92. $strQuery = preg_replace_callback("/&#(\\d+);/", array($this, "chr"), $strQuery);
  93. $bTagsSearch = false;
  94. }
  95. if (!array_key_exists("STEMMING", $aParamsEx))
  96. $aParamsEx["STEMMING"] = COption::GetOptionString("search", "use_stemming") == "Y";
  97. $this->Query = new CSearchQuery("and", "yes", 0, $arParams["SITE_ID"]);
  98. if ($this->_opt_NO_WORD_LOGIC)
  99. $this->Query->no_bool_lang = true;
  100. $query = $this->Query->GetQueryString((BX_SEARCH_VERSION > 1? "sct": "sc").".SEARCHABLE_CONTENT", $strQuery, $bTagsSearch, $aParamsEx["STEMMING"], $this->_opt_ERROR_ON_EMPTY_STEM);
  101. $fullTextParams = $aParamsEx;
  102. if (!isset($fullTextParams["LIMIT"]))
  103. $fullTextParams["LIMIT"] = $this->limit;
  104. $fullTextParams["OFFSET"] = $this->offset;
  105. $fullTextParams["QUERY_OBJECT"] = $this->Query;
  106. $result = CSearchFullText::getInstance()->search($arParams, $aSort, $fullTextParams, $bTagsCloud);
  107. if (is_array($result))
  108. {
  109. $this->error = CSearchFullText::getInstance()->getErrorText();
  110. $this->errorno = CSearchFullText::getInstance()->getErrorNumber();
  111. $this->formatter = CSearchFullText::getInstance()->getRowFormatter();
  112. if ($this->errorno > 0)
  113. return;
  114. }
  115. else
  116. {
  117. if (!$query || trim($query) == '')
  118. {
  119. if ($bTagsCloud)
  120. {
  121. $query = "1=1";
  122. }
  123. else
  124. {
  125. $this->error = $this->Query->error;
  126. $this->errorno = $this->Query->errorno;
  127. return;
  128. }
  129. }
  130. if (mb_strlen($query) > 2000)
  131. {
  132. $this->error = GetMessage("SEARCH_ERROR4");
  133. $this->errorno = 4;
  134. return;
  135. }
  136. }
  137. foreach (GetModuleEvents("search", "OnSearch", true) as $arEvent)
  138. {
  139. $r = "";
  140. if ($bTagsSearch)
  141. {
  142. if($strTags <> '')
  143. {
  144. $r = ExecuteModuleEventEx($arEvent, array("tags:".$strTags));
  145. }
  146. }
  147. else
  148. {
  149. $r = ExecuteModuleEventEx($arEvent, array($strQuery));
  150. }
  151. if ($r <> "")
  152. $this->url_add_params[] = $r;
  153. }
  154. if (is_array($result))
  155. {
  156. $r = new CDBResult;
  157. $r->InitFromArray($result);
  158. }
  159. elseif (
  160. BX_SEARCH_VERSION > 1
  161. && !empty($this->Query->m_stemmed_words_id)
  162. && is_array($this->Query->m_stemmed_words_id)
  163. && array_sum($this->Query->m_stemmed_words_id) === 0
  164. )
  165. {
  166. $r = new CDBResult;
  167. $r->InitFromArray(array());
  168. }
  169. else
  170. {
  171. $this->strSqlWhere = "";
  172. $bIncSites = false;
  173. $arSqlWhere = array();
  174. if (is_array($aParamsEx) && !empty($aParamsEx))
  175. {
  176. foreach ($aParamsEx as $aParamEx)
  177. {
  178. $strSqlWhere = CSearch::__PrepareFilter($aParamEx, $bIncSites);
  179. if ($strSqlWhere != "")
  180. $arSqlWhere[] = $strSqlWhere;
  181. }
  182. }
  183. if (!empty($arSqlWhere))
  184. {
  185. $arSqlWhere = array(
  186. "\n\t\t\t\t(".implode(")\n\t\t\t\t\tOR(", $arSqlWhere)."\n\t\t\t\t)",
  187. );
  188. }
  189. $strSqlWhere = CSearch::__PrepareFilter($arParams, $bIncSites);
  190. if ($strSqlWhere != "")
  191. array_unshift($arSqlWhere, $strSqlWhere);
  192. $strSqlOrder = $this->__PrepareSort($aSort, "sc.", $bTagsCloud);
  193. if (!array_key_exists("USE_TF_FILTER", $aParamsEx))
  194. $aParamsEx["USE_TF_FILTER"] = COption::GetOptionString("search", "use_tf_cache") == "Y";
  195. $bStem = !$bTagsSearch && count($this->Query->m_stemmed_words) > 0;
  196. //calculate freq of the word on the whole site_id
  197. if ($bStem && count($this->Query->m_stemmed_words))
  198. {
  199. $arStat = $this->GetFreqStatistics($this->Query->m_lang, $this->Query->m_stemmed_words, $arParams["SITE_ID"]);
  200. $this->tf_hwm_site_id = ($arParams["SITE_ID"] <> ''? $arParams["SITE_ID"]: "");
  201. //we'll make filter by it's contrast
  202. if (!$bTagsCloud && $aParamsEx["USE_TF_FILTER"])
  203. {
  204. $hwm = false;
  205. foreach ($this->Query->m_stemmed_words as $i => $stem)
  206. {
  207. if (!array_key_exists($stem, $arStat))
  208. {
  209. $hwm = 0;
  210. break;
  211. }
  212. elseif ($hwm === false)
  213. {
  214. $hwm = $arStat[$stem]["TF"];
  215. }
  216. elseif ($hwm > $arStat[$stem]["TF"])
  217. {
  218. $hwm = $arStat[$stem]["TF"];
  219. }
  220. }
  221. if ($hwm > 0)
  222. {
  223. $arSqlWhere[] = "st.TF >= ".number_format($hwm, 2, ".", "");
  224. $this->tf_hwm = $hwm;
  225. }
  226. }
  227. }
  228. if (!empty($arSqlWhere))
  229. {
  230. $this->strSqlWhere = "\n\t\t\t\tAND (\n\t\t\t\t\t(".implode(")\n\t\t\t\t\tAND(", $arSqlWhere).")\n\t\t\t\t)";
  231. }
  232. if ($bTagsCloud)
  233. $strSql = $this->tagsMakeSQL($query, $this->strSqlWhere, $strSqlOrder, $bIncSites, $bStem, $aParamsEx["LIMIT"]);
  234. else
  235. $strSql = $this->MakeSQL($query, $this->strSqlWhere, $strSqlOrder, $bIncSites, $bStem);
  236. $r = $DB->Query($strSql, false, "File: ".__FILE__."<br>Line: ".__LINE__);
  237. }
  238. parent::__construct($r);
  239. }
  240. function SetOptions($arOptions)
  241. {
  242. if (array_key_exists("ERROR_ON_EMPTY_STEM", $arOptions))
  243. $this->_opt_ERROR_ON_EMPTY_STEM = $arOptions["ERROR_ON_EMPTY_STEM"] === true;
  244. if (array_key_exists("NO_WORD_LOGIC", $arOptions))
  245. $this->_opt_NO_WORD_LOGIC = $arOptions["NO_WORD_LOGIC"] === true;
  246. }
  247. function SetOffset($offset)
  248. {
  249. $this->offset = (int)$offset;
  250. }
  251. function SetLimit($limit)
  252. {
  253. $this->limit = (int)$limit;
  254. }
  255. function GetFilterMD5()
  256. {
  257. $perm = CSearch::CheckPermissions("sc.ID");
  258. $sql = preg_replace("/(DATE_FROM|DATE_TO|DATE_CHANGE)(\\s+IS\\s+NOT\\s+NULL|\\s+IS\\s+NULL|\\s*[<>!=]+\\s*'.*?')/im", "", $this->strSqlWhere);
  259. return md5($perm.$sql.$this->strTags);
  260. }
  261. public static function chr($a)
  262. {
  263. return chr($a[1]);
  264. }
  265. function GetFreqStatistics($lang_id, $arStem, $site_id = "")
  266. {
  267. $DB = CDatabase::GetModuleConnection('search');
  268. $sql_site_id = $DB->ForSQL($site_id);
  269. $sql_lang_id = $DB->ForSQL($lang_id);
  270. $sql_stem = array();
  271. foreach ($arStem as $stem)
  272. $sql_stem[] = $DB->ForSQL($stem);
  273. $limit = COption::GetOptionInt("search", "max_result_size");
  274. if ($limit < 1)
  275. $limit = 500;
  276. $arResult = array();
  277. foreach ($arStem as $stem)
  278. $arResult[$stem] = array(
  279. "STEM" => false,
  280. "FREQ" => 0,
  281. "TF" => 0,
  282. "STEM_COUNT" => 0,
  283. "TF_SUM" => 0,
  284. );
  285. if (BX_SEARCH_VERSION > 1)
  286. $strSql = "
  287. SELECT s.ID, s.STEM, FREQ, TF
  288. FROM b_search_content_freq f
  289. inner join b_search_stem s on s.ID = f.STEM
  290. WHERE LANGUAGE_ID = '".$sql_lang_id."'
  291. AND s.STEM in ('".implode("','", $sql_stem)."')
  292. AND ".($site_id <> ''? "SITE_ID = '".$sql_site_id."'": "SITE_ID IS NULL")."
  293. ORDER BY STEM
  294. ";
  295. else
  296. $strSql = "
  297. SELECT STEM ID,STEM, FREQ, TF
  298. FROM b_search_content_freq
  299. WHERE LANGUAGE_ID = '".$sql_lang_id."'
  300. AND STEM in ('".implode("','", $sql_stem)."')
  301. AND ".($site_id <> ''? "SITE_ID = '".$sql_site_id."'": "SITE_ID IS NULL")."
  302. ORDER BY STEM
  303. ";
  304. $rs = $DB->Query($strSql, false, "File: ".__FILE__."<br>Line: ".__LINE__);
  305. while ($ar = $rs->Fetch())
  306. {
  307. if ($ar["TF"] <> '')
  308. $arResult[$ar["STEM"]] = $ar;
  309. }
  310. $arMissed = array();
  311. foreach ($arResult as $stem => $ar)
  312. if (!$ar["STEM"])
  313. $arMissed[] = $DB->ForSQL($stem);
  314. if (count($arMissed) > 0)
  315. {
  316. if (BX_SEARCH_VERSION > 1)
  317. $strSql = "
  318. SELECT s.ID, s.STEM, floor(st.TF/100) BUCKET, sum(st.TF/10000) TF_SUM, count(*) STEM_COUNT
  319. FROM
  320. b_search_content_stem st
  321. inner join b_search_stem s on s.ID = st.STEM
  322. ".($site_id <> ''? "INNER JOIN b_search_content_site scsite ON scsite.SEARCH_CONTENT_ID = st.SEARCH_CONTENT_ID AND scsite.SITE_ID = '".$sql_site_id."'": "")."
  323. WHERE st.LANGUAGE_ID = '".$sql_lang_id."'
  324. AND s.STEM in ('".implode("','", $arMissed)."')
  325. GROUP BY s.ID, s.STEM, floor(st.TF/100)
  326. ORDER BY s.ID, s.STEM, floor(st.TF/100) DESC
  327. ";
  328. else
  329. $strSql = "
  330. SELECT st.STEM ID, st.STEM, floor(st.TF*100) BUCKET, sum(st.TF) TF_SUM, count(*) STEM_COUNT
  331. FROM
  332. b_search_content_stem st
  333. ".($site_id <> ''? "INNER JOIN b_search_content_site scsite ON scsite.SEARCH_CONTENT_ID = st.SEARCH_CONTENT_ID AND scsite.SITE_ID = '".$sql_site_id."'": "")."
  334. WHERE st.LANGUAGE_ID = '".$sql_lang_id."'
  335. AND st.STEM in ('".implode("','", $arMissed)."')
  336. GROUP BY st.STEM, floor(st.TF*100)
  337. ORDER BY st.STEM, floor(st.TF*100) DESC
  338. ";
  339. $rs = $DB->Query($strSql, false, "File: ".__FILE__."<br>Line: ".__LINE__);
  340. while ($ar = $rs->Fetch())
  341. {
  342. $stem = $ar["STEM"];
  343. if ($arResult[$stem]["STEM_COUNT"] < $limit)
  344. $arResult[$stem]["TF"] = $ar["BUCKET"] / 100.0;
  345. $arResult[$stem]["STEM_COUNT"] += $ar["STEM_COUNT"];
  346. $arResult[$stem]["TF_SUM"] += $ar["TF_SUM"];
  347. $arResult[$stem]["DO_INSERT"] = true;
  348. $arResult[$stem]["ID"] = $ar["ID"];
  349. }
  350. }
  351. foreach ($arResult as $stem => $ar)
  352. {
  353. if ($ar["DO_INSERT"])
  354. {
  355. $FREQ = intval(defined("search_range_by_sum_tf")? $ar["TF_SUM"]: $ar["STEM_COUNT"]);
  356. $strSql = "
  357. UPDATE b_search_content_freq
  358. SET FREQ=".$FREQ.", TF=".number_format($ar["TF"], 2, ".", "")."
  359. WHERE LANGUAGE_ID='".$sql_lang_id."'
  360. AND ".($site_id <> ''? "SITE_ID = '".$sql_site_id."'": "SITE_ID IS NULL")."
  361. AND STEM='".$DB->ForSQL($ar["ID"])."'
  362. ";
  363. $rsUpdate = $DB->Query($strSql, false, "File: ".__FILE__."<br>Line: ".__LINE__);
  364. if ($rsUpdate->AffectedRowsCount() <= 0)
  365. {
  366. $strSql = "
  367. INSERT INTO b_search_content_freq
  368. (STEM, LANGUAGE_ID, SITE_ID, FREQ, TF)
  369. VALUES
  370. ('".$DB->ForSQL($ar["ID"])."', '".$sql_lang_id."', ".($site_id <> ''? "'".$sql_site_id."'": "NULL").", ".$FREQ.", ".number_format($ar["TF"], 2, ".", "").")
  371. ";
  372. $rsInsert = $DB->Query($strSql, true);
  373. }
  374. }
  375. }
  376. return $arResult;
  377. }
  378. function Repl($strCond, $strType, $strWh)
  379. {
  380. $l = mb_strlen($strCond);
  381. if ($this->Query->bStemming)
  382. {
  383. $arStemInfo = stemming_init($this->Query->m_lang);
  384. $pcreLettersClass = "[".$arStemInfo["pcre_letters"]."]";
  385. $strWhUpp = stemming_upper($strWh, $this->Query->m_lang);
  386. }
  387. else
  388. {
  389. $strWhUpp = ToUpper($strWh);
  390. }
  391. $strCondUpp = ToUpper($strCond);
  392. $pos = 0;
  393. do
  394. {
  395. $pos = mb_strpos($strWhUpp, $strCondUpp, $pos);
  396. //Check if we are in the middle of the numeric entity
  397. while (
  398. $pos !== false &&
  399. preg_match("/^[0-9]+;/", mb_substr($strWh, $pos)) &&
  400. preg_match("/^[0-9]+#&/", strrev(mb_substr($strWh, 0, $pos + mb_strlen($strCond))))
  401. )
  402. {
  403. $pos = mb_strpos($strWhUpp, $strCondUpp, $pos + 1);
  404. }
  405. if ($pos === false) break;
  406. if ($strType == "STEM")
  407. {
  408. $lw = mb_strlen($strWhUpp);
  409. for ($s = $pos; $s >= 0; $s--)
  410. {
  411. if (!preg_match("/$pcreLettersClass/".BX_UTF_PCRE_MODIFIER, mb_substr($strWhUpp, $s, 1)))
  412. break;
  413. }
  414. $s++;
  415. for ($e = $pos; $e < $lw; $e++)
  416. {
  417. if (!preg_match("/$pcreLettersClass/".BX_UTF_PCRE_MODIFIER, mb_substr($strWhUpp, $e, 1)))
  418. break;
  419. }
  420. $e--;
  421. $a = stemming(mb_substr($strWhUpp, $s, $e - $s + 1), $this->Query->m_lang, true);
  422. foreach ($a as $stem => $cnt)
  423. {
  424. if ($stem == $strCondUpp)
  425. {
  426. $strWh = mb_substr($strWh, 0, $pos)."%^%".mb_substr($strWh, $pos, $e - $pos + 1)."%/^%".mb_substr($strWh, $e + 1);
  427. $strWhUpp = mb_substr($strWhUpp, 0, $pos)."%^%".str_repeat(" ", $e - $pos + 1)."%/^%".mb_substr($strWhUpp, $e + 1);
  428. $pos += 7 + $e - $pos + 1;
  429. }
  430. }
  431. }
  432. else
  433. {
  434. $strWh = mb_substr($strWh, 0, $pos)."%^%".mb_substr($strWh, $pos, $l)."%/^%".mb_substr($strWh, $pos + $l);
  435. $strWhUpp = mb_substr($strWhUpp, 0, $pos)."%^%".str_repeat(" ", $l)."%/^%".mb_substr($strWhUpp, $pos + $l);
  436. $pos += 7 + $l;
  437. }
  438. $pos += 1;
  439. } while ($pos < mb_strlen($strWhUpp));
  440. return $strWh;
  441. }
  442. function PrepareSearchResult($str)
  443. {
  444. //$words - contains what we will highlight
  445. $words = array();
  446. foreach ($this->Query->m_words as $v)
  447. {
  448. $v = ToUpper($v);
  449. $words[$v] = "KAV";
  450. if (mb_strpos($v, "\"") !== false)
  451. $words[str_replace("\"", "&QUOT;", $v)] = "KAV";
  452. }
  453. foreach ($this->Query->m_stemmed_words as $v)
  454. $words[ToUpper($v)] = "STEM";
  455. //Prepare upper case version of the string
  456. if ($this->Query->bStemming)
  457. {
  458. //And add missing stemming words
  459. $arStemInfo = stemming_init($this->Query->m_lang);
  460. $a = stemming($this->Query->m_query, $this->Query->m_lang, true);
  461. foreach ($a as $stem => $cnt)
  462. {
  463. if (!preg_match("/cut[56]/i", $stem))
  464. $words[$stem] = "STEM";
  465. }
  466. $pcreLettersClass = "[".$arStemInfo["pcre_letters"]."]";
  467. $strUpp = stemming_upper($str, $this->Query->m_lang);
  468. }
  469. else
  470. {
  471. $strUpp = ToUpper($str);
  472. $pcreLettersClass = "";
  473. }
  474. $wordsCount = count($words);
  475. //We'll use regexp to find positions of the words in the text
  476. $pregMask = "";
  477. foreach ($words as $search => $type)
  478. {
  479. if ($type == "STEM")
  480. $pregMask = "(?<!".$pcreLettersClass.")".preg_quote($search, "/").$pcreLettersClass."*|".$pregMask;
  481. else
  482. $pregMask = $pregMask."|".preg_quote($search, "/");
  483. }
  484. $pregMask = trim($pregMask, "|");
  485. $arPos = array(); //This will contain positions of the first occurrence
  486. $arPosW = array(); //This is "running" words array
  487. $arPosP = array(); //and their positions
  488. $arPosLast = false; //Best found combination of the positions
  489. $matches = array();
  490. if (preg_match_all("/(".$pregMask.")/i".BX_UTF_PCRE_MODIFIER, $strUpp, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE))
  491. {
  492. foreach ($matches as $oneCase)
  493. {
  494. $search = null;
  495. if (isset($words[$oneCase[0][0]]))
  496. {
  497. $search = $oneCase[0][0];
  498. }
  499. else
  500. {
  501. $a = stemming($oneCase[0][0], $this->Query->m_lang, true);
  502. foreach ($a as $stem => $cnt)
  503. {
  504. if (isset($words[$stem]))
  505. {
  506. $search = $stem;
  507. break;
  508. }
  509. }
  510. }
  511. if (isset($search))
  512. {
  513. $p = $oneCase[0][1];
  514. if (!isset($arPos[$search]))
  515. $arPos[$search] = $p;
  516. //Add to the tail of the running window
  517. $arPosP[] = $p;
  518. $arPosW[] = $search;
  519. $cc = count($arPosW);
  520. if ($cc >= $wordsCount)
  521. {
  522. //This cuts the tail of the running window
  523. while ($cc > $wordsCount)
  524. {
  525. array_shift($arPosW);
  526. array_shift($arPosP);
  527. $cc--;
  528. }
  529. //Check if all the words present in the current window
  530. if (count(array_unique($arPosW)) == $wordsCount)
  531. {
  532. //And check if positions is the best
  533. if (
  534. !$arPosLast
  535. || (
  536. (max($arPosP) - min($arPosP)) < (max($arPosLast) - min($arPosLast))
  537. )
  538. )
  539. $arPosLast = $arPosP;
  540. }
  541. }
  542. }
  543. }
  544. }
  545. if ($arPosLast)
  546. $arPos = $arPosLast;
  547. //Nothing found just cut some text
  548. if (empty($arPos))
  549. {
  550. $str_len = mb_strlen($str);
  551. $pos_end = 500;
  552. while (($pos_end < $str_len) && (mb_strpos(" ,.\n\r", mb_substr($str, $pos_end, 1)) === false))
  553. $pos_end++;
  554. return mb_substr($str, 0, $pos_end).($pos_end < $str_len? "..." : "");
  555. }
  556. sort($arPos);
  557. $str_len = CUtil::BinStrlen($str);
  558. $delta = 250 / count($arPos);
  559. $arOtr = array();
  560. //Have to do it two times because Positions eat each other
  561. for ($i = 0; $i < 2; $i++)
  562. {
  563. $arOtr = array();
  564. $last_pos = -1;
  565. foreach ($arPos as $pos_mid)
  566. {
  567. //Find where sentence begins
  568. $pos_beg = $pos_mid - $delta;
  569. if ($pos_beg <= 0)
  570. $pos_beg = 0;
  571. while (($pos_beg > 0) && (mb_strpos(" ,.!?\n\r", CUtil::BinSubstr($str, $pos_beg, 1)) === false))
  572. $pos_beg--;
  573. //Find where sentence ends
  574. $pos_end = $pos_mid + $delta;
  575. if ($pos_end > $str_len)
  576. $pos_end = $str_len;
  577. while (($pos_end < $str_len) && (mb_strpos(" ,.!?\n\r", CUtil::BinSubstr($str, $pos_end, 1)) === false))
  578. $pos_end++;
  579. if ($pos_beg <= $last_pos)
  580. $arOtr[count($arOtr) - 1][1] = $pos_end;
  581. else
  582. $arOtr[] = array($pos_beg, $pos_end);
  583. $last_pos = $pos_end;
  584. }
  585. //Adjust length of the text
  586. $delta = 250 / count($arOtr);
  587. }
  588. $str_result = "";
  589. foreach ($arOtr as $borders)
  590. {
  591. $str_result .= ($borders[0] <= 0? "": " ...")
  592. .CUtil::BinSubstr($str, $borders[0], $borders[1] - $borders[0] + 1)
  593. .($borders[1] >= $str_len? "": "... ");
  594. }
  595. foreach ($words as $search => $type)
  596. {
  597. $str_result = $this->Repl($search, $type, $str_result);
  598. }
  599. $str_result = str_replace("%/^%", "</b>", str_replace("%^%", "<b>", $str_result));
  600. return $str_result;
  601. }
  602. function NavStart($nPageSize = 0, $bShowAll = true, $iNumPage = false)
  603. {
  604. parent::NavStart($nPageSize, $bShowAll, $iNumPage);
  605. if (COption::GetOptionString("search", "stat_phrase") == "Y")
  606. {
  607. $this->Statistic = new CSearchStatistic($this->strQueryText, $this->strTagsText);
  608. $this->Statistic->PhraseStat($this->NavRecordCount, $this->NavPageNomer);
  609. if ($this->Statistic->phrase_id)
  610. $this->url_add_params[] = "sphrase_id=".$this->Statistic->phrase_id;
  611. }
  612. }
  613. function Fetch()
  614. {
  615. static $arSite = array();
  616. $DB = CDatabase::GetModuleConnection('search');
  617. $r = parent::Fetch();
  618. if ($r && $this->formatter)
  619. {
  620. $r = $this->formatter->format($r);
  621. if (!$r)
  622. return $this->Fetch();
  623. }
  624. if ($r)
  625. {
  626. $site_id = $r["SITE_ID"];
  627. if (!isset($arSite[$site_id]))
  628. {
  629. $rsSite = CSite::GetList('', '', array("ID" => $site_id));
  630. $arSite[$site_id] = $rsSite->Fetch();
  631. }
  632. $r["DIR"] = $arSite[$site_id]["DIR"];
  633. $r["SERVER_NAME"] = $arSite[$site_id]["SERVER_NAME"];
  634. if ($r["SITE_URL"] <> '')
  635. $r["URL"] = $r["SITE_URL"];
  636. if (mb_substr($r["URL"], 0, 1) == "=")
  637. {
  638. foreach (GetModuleEvents("search", "OnSearchGetURL", true) as $arEvent)
  639. {
  640. $newUrl = ExecuteModuleEventEx($arEvent, array($r));
  641. if (isset($newUrl))
  642. {
  643. $r["URL"] = $newUrl;
  644. }
  645. }
  646. }
  647. $r["URL"] = str_replace(
  648. array("#LANG#", "#SITE_DIR#", "#SERVER_NAME#"),
  649. array($r["DIR"], $r["DIR"], $r["SERVER_NAME"]),
  650. $r["URL"]
  651. );
  652. $r["URL"] = preg_replace("'(?<!:)/+'s", "/", $r["URL"]);
  653. $r["URL_WO_PARAMS"] = $r["URL"];
  654. $w = $this->Query->m_words;
  655. if (count($this->url_add_params))
  656. {
  657. $p1 = mb_strpos($r["URL"], "?");
  658. if ($p1 === false)
  659. $ch = "?";
  660. else
  661. $ch = "&";
  662. $p2 = mb_strpos($r["URL"], "#", $p1);
  663. if ($p2 === false)
  664. {
  665. $r["URL"] = $r["URL"].$ch.implode("&", $this->url_add_params);
  666. }
  667. else
  668. {
  669. $r["URL"] = mb_substr($r["URL"], 0, $p2).$ch.implode("&", $this->url_add_params).mb_substr($r["URL"], $p2);
  670. }
  671. }
  672. if (!array_key_exists("TITLE_FORMATED", $r) && array_key_exists("TITLE", $r))
  673. {
  674. $r["TITLE_FORMATED"] = $this->PrepareSearchResult(htmlspecialcharsEx($r["TITLE"]));
  675. $r["TITLE_FORMATED_TYPE"] = "html";
  676. $r["TAGS_FORMATED"] = tags_prepare($r["TAGS"], SITE_ID);
  677. if ($r["BODY"])
  678. {
  679. $r["BODY_FORMATED"] = $this->PrepareSearchResult(htmlspecialcharsEx($r["BODY"]));
  680. $r["BODY_FORMATED_TYPE"] = "html";
  681. }
  682. else
  683. {
  684. $max_body_size = COption::GetOptionInt("search", "max_body_size");
  685. $sqlBody = $max_body_size > 0? "left(BODY,".$max_body_size.") as BODY": "BODY";
  686. $rsBody = $DB->Query("select $sqlBody from b_search_content where ID=".$r["ID"]);
  687. if ($arBody = $rsBody->Fetch())
  688. {
  689. $r["BODY_FORMATED"] = $this->PrepareSearchResult(htmlspecialcharsEx($arBody["BODY"]));
  690. $r["BODY_FORMATED_TYPE"] = "html";
  691. }
  692. }
  693. }
  694. }
  695. return $r;
  696. }
  697. public static function CheckPath($path)
  698. {
  699. static $SEARCH_MASKS_CACHE = false;
  700. if (!is_array($SEARCH_MASKS_CACHE))
  701. {
  702. $arSearch = array("\\", ".", "?", "*", "'");
  703. $arReplace = array("/", "\\.", ".", ".*?", "\\'");
  704. $arInc = array();
  705. $inc = str_replace(
  706. $arSearch,
  707. $arReplace,
  708. COption::GetOptionString("search", "include_mask")
  709. );
  710. $arIncTmp = explode(";", $inc);
  711. foreach ($arIncTmp as $mask)
  712. {
  713. $mask = trim($mask);
  714. if($mask <> '')
  715. {
  716. $arInc[] = "'^".$mask."$'";
  717. }
  718. }
  719. $arFullExc = array();
  720. $arExc = array();
  721. $exc = str_replace(
  722. $arSearch,
  723. $arReplace,
  724. COption::GetOptionString("search", "exclude_mask")
  725. );
  726. $arExcTmp = explode(";", $exc);
  727. foreach ($arExcTmp as $mask)
  728. {
  729. $mask = trim($mask);
  730. if($mask <> '')
  731. {
  732. if(preg_match("#^/[a-z0-9_.\\\\]+/#i", $mask))
  733. {
  734. $arFullExc[] = "'^".$mask."$'".BX_UTF_PCRE_MODIFIER;
  735. }
  736. else
  737. {
  738. $arExc[] = "'^".$mask."$'".BX_UTF_PCRE_MODIFIER;
  739. }
  740. }
  741. }
  742. $SEARCH_MASKS_CACHE = Array(
  743. "full_exc" => $arFullExc,
  744. "exc" => $arExc,
  745. "inc" => $arInc
  746. );
  747. }
  748. $file = end(explode('/', $path)); //basename
  749. if (strncmp($file, ".", 1) == 0)
  750. return 0;
  751. foreach ($SEARCH_MASKS_CACHE["full_exc"] as $mask)
  752. if (preg_match($mask, $path))
  753. return false;
  754. foreach ($SEARCH_MASKS_CACHE["exc"] as $mask)
  755. if (preg_match($mask, $path))
  756. return 0;
  757. foreach ($SEARCH_MASKS_CACHE["inc"] as $mask)
  758. if (preg_match($mask, $path))
  759. return true;
  760. return 0;
  761. }
  762. public static function GetGroupCached()
  763. {
  764. static $SEARCH_CACHED_GROUPS = false;
  765. if (!is_array($SEARCH_CACHED_GROUPS))
  766. {
  767. $SEARCH_CACHED_GROUPS = Array();
  768. $db_groups = CGroup::GetList('id', 'asc');
  769. while ($g = $db_groups->Fetch())
  770. {
  771. $group_id = intval($g["ID"]);
  772. if ($group_id > 1)
  773. $SEARCH_CACHED_GROUPS[$group_id] = $group_id;
  774. }
  775. }
  776. return $SEARCH_CACHED_GROUPS;
  777. }
  778. public static function QueryMnogoSearch(&$xml)
  779. {
  780. $SITE = COption::GetOptionString("search", "mnogosearch_url", "www.mnogosearch.org");
  781. $PATH = COption::GetOptionString("search", "mnogosearch_path", "");
  782. $PORT = COption::GetOptionString("search", "mnogosearch_port", "80");
  783. $QUERY_STR = 'document='.urlencode($xml);
  784. $strRequest = "POST ".$PATH." HTTP/1.0\r\n";
  785. $strRequest .= "User-Agent: BitrixSM\r\n";
  786. $strRequest .= "Accept: */*\r\n";
  787. $strRequest .= "Host: $SITE\r\n";
  788. $strRequest .= "Accept-Language: en\r\n";
  789. $strRequest .= "Content-type: application/x-www-form-urlencoded\r\n";
  790. $strRequest .= "Content-length: ".mb_strlen($QUERY_STR)."\r\n";
  791. $strRequest .= "\r\n";
  792. $strRequest .= $QUERY_STR;
  793. $strRequest .= "\r\n";
  794. $arAll = "";
  795. $errno = 0;
  796. $errstr = "";
  797. $FP = fsockopen($SITE, $PORT, $errno, $errstr, 120);
  798. if ($FP)
  799. {
  800. fputs($FP, $strRequest);
  801. while (($line = fgets($FP, 4096)) && $line != "\r\n") ;
  802. while ($line = fread($FP, 4096))
  803. $arAll .= $line;
  804. fclose($FP);
  805. }
  806. return $arAll;
  807. }
  808. //////////////////////////////////
  809. //reindex the whole server content
  810. //$bFull = true - no not check change_date. all index tables will be truncated
  811. // = false - add new ones. update changed and delete deleted.
  812. public static function ReIndexAll($bFull = false, $max_execution_time = 0, $NS = Array(), $clear_suggest = false)
  813. {
  814. global $APPLICATION;
  815. $DB = CDatabase::GetModuleConnection('search');
  816. @set_time_limit(0);
  817. if (!is_array($NS))
  818. $NS = Array();
  819. if ($max_execution_time <= 0)
  820. {
  821. $NS_OLD = $NS;
  822. $NS = Array("CLEAR" => "N", "MODULE" => "", "ID" => "", "SESS_ID" => md5(uniqid("")));
  823. if ($NS_OLD["SITE_ID"] != "") $NS["SITE_ID"] = $NS_OLD["SITE_ID"];
  824. if ($NS_OLD["MODULE_ID"] != "") $NS["MODULE_ID"] = $NS_OLD["MODULE_ID"];
  825. }
  826. $NS["CNT"] = intval($NS["CNT"]);
  827. if (!$bFull && mb_strlen($NS["SESS_ID"]) != 32)
  828. $NS["SESS_ID"] = md5(uniqid(""));
  829. $p1 = getmicrotime();
  830. $DB->StartTransaction();
  831. CSearch::ReindexLock();
  832. if ($NS["CLEAR"] != "Y")
  833. {
  834. if ($bFull)
  835. {
  836. foreach (GetModuleEvents("search", "OnBeforeFullReindexClear", true) as $arEvent)
  837. ExecuteModuleEventEx($arEvent);
  838. CSearchTags::CleanCache();
  839. $DB->Query("TRUNCATE TABLE b_search_content_param", false, "File: ".__FILE__."<br>Line: ".__LINE__);
  840. $DB->Query("TRUNCATE TABLE b_search_content_site", false, "File: ".__FILE__."<br>Line: ".__LINE__);
  841. $DB->Query("TRUNCATE TABLE b_search_content_right", false, "File: ".__FILE__."<br>Line: ".__LINE__);
  842. $DB->Query("TRUNCATE TABLE b_search_content_title", false, "File: ".__FILE__."<br>Line: ".__LINE__);
  843. $DB->Query("TRUNCATE TABLE b_search_tags", false, "File: ".__FILE__."<br>Line: ".__LINE__);
  844. $DB->Query("TRUNCATE TABLE b_search_content_freq", false, "File: ".__FILE__."<br>Line: ".__LINE__);
  845. $DB->Query("TRUNCATE TABLE b_search_content", false, "File: ".__FILE__."<br>Line: ".__LINE__);
  846. $DB->Query("TRUNCATE TABLE b_search_suggest", false, "File: ".__FILE__."<br>Line: ".__LINE__);
  847. $DB->Query("TRUNCATE TABLE b_search_user_right", false, "File: ".__FILE__."<br>Line: ".__LINE__);
  848. CSearchFullText::getInstance()->truncate();
  849. COption::SetOptionString("search", "full_reindex_required", "N");
  850. }
  851. elseif ($clear_suggest)
  852. {
  853. $DB->Query("TRUNCATE TABLE b_search_suggest", false, "File: ".__FILE__."<br>Line: ".__LINE__);
  854. $DB->Query("TRUNCATE TABLE b_search_user_right", false, "File: ".__FILE__."<br>Line: ".__LINE__);
  855. $DB->Query("TRUNCATE TABLE b_search_content_freq", false, "File: ".__FILE__."<br>Line: ".__LINE__);
  856. }
  857. }
  858. $NS["CLEAR"] = "Y";
  859. clearstatcache();
  860. if (
  861. ($NS["MODULE"] == "" || $NS["MODULE"] == "main") &&
  862. ($NS["MODULE_ID"] == "" || $NS["MODULE_ID"] == "main")
  863. )
  864. {
  865. $arLangDirs = Array();
  866. $arFilter = Array("ACTIVE" => "Y");
  867. if ($NS["SITE_ID"] != "")
  868. $arFilter["ID"] = $NS["SITE_ID"];
  869. $r = CSite::GetList('', '', $arFilter);
  870. while ($arR = $r->Fetch())
  871. {
  872. $path = rtrim($arR["DIR"], "/");
  873. $arLangDirs[$arR["ABS_DOC_ROOT"]."/".$path."/"] = $arR;
  874. }
  875. //get rid of duplicates
  876. $dub = Array();
  877. foreach ($arLangDirs as $path => $arR)
  878. {
  879. foreach ($arLangDirs as $path2 => $arR2)
  880. {
  881. if ($path == $path2) continue;
  882. if (mb_substr($path, 0, mb_strlen($path2)) == $path2)
  883. $dub[] = $path;
  884. }
  885. }
  886. foreach ($dub as $p)
  887. unset($arLangDirs[$p]);
  888. foreach ($arLangDirs as $arR)
  889. {
  890. $site = $arR["ID"];
  891. $path = rtrim($arR["DIR"], "/");
  892. $site_path = $site."|".$path."/";
  893. if (
  894. $max_execution_time > 0
  895. && $NS["MODULE"] == "main"
  896. && mb_substr($NS["ID"]."/", 0, mb_strlen($site_path)) != $site_path
  897. )
  898. continue;
  899. //for every folder
  900. CSearch::RecurseIndex(Array($site, $path), $max_execution_time, $NS);
  901. if (
  902. $max_execution_time > 0
  903. && $NS["MODULE"] <> ''
  904. )
  905. {
  906. $DB->Commit();
  907. return $NS;
  908. }
  909. }
  910. }
  911. $p1 = getmicrotime();
  912. //for every who wants to reindex
  913. $oCallBack = new CSearchCallback;
  914. $oCallBack->max_execution_time = $max_execution_time;
  915. foreach (GetModuleEvents("search", "OnReindex", true) as $arEvent)
  916. {
  917. if ($NS["MODULE_ID"] != "" && $NS["MODULE_ID"] != $arEvent["TO_MODULE_ID"]) continue;
  918. if ($max_execution_time > 0 && $NS["MODULE"] <> '' && $NS["MODULE"] != "main" && $NS["MODULE"] != $arEvent["TO_MODULE_ID"]) continue;
  919. //here we get recordset
  920. $oCallBack->MODULE = $arEvent["TO_MODULE_ID"];
  921. $oCallBack->CNT = &$NS["CNT"];
  922. $oCallBack->SESS_ID = $NS["SESS_ID"];
  923. $r = &$oCallBack;
  924. $arResult = ExecuteModuleEventEx($arEvent, array($NS, $r, "Index"));
  925. if (is_array($arResult)) //old way
  926. {
  927. foreach ($arResult as $arFields)
  928. {
  929. $ID = $arFields["ID"];
  930. if ($ID <> '')
  931. {
  932. unset($arFields["ID"]);
  933. $NS["CNT"]++;
  934. CSearch::Index($arEvent["TO_MODULE_ID"], $ID, $arFields, false, $NS["SESS_ID"]);
  935. }
  936. }
  937. }
  938. else //new method
  939. {
  940. if ($max_execution_time > 0 && $arResult !== false && mb_strlen(".".$arResult) > 1)
  941. {
  942. $DB->Commit();
  943. return Array(
  944. "MODULE" => $arEvent["TO_MODULE_ID"],
  945. "CNT" => $oCallBack->CNT,
  946. "ID" => $arResult,
  947. "CLEAR" => $NS["CLEAR"],
  948. "SESS_ID" => $NS["SESS_ID"],
  949. "SITE_ID" => $NS["SITE_ID"],
  950. "MODULE_ID" => $NS["MODULE_ID"],
  951. );
  952. }
  953. }
  954. $NS["MODULE"] = "";
  955. }
  956. if (!$bFull)
  957. {
  958. CSearch::DeleteOld($NS["SESS_ID"], $NS["MODULE_ID"], $NS["SITE_ID"]);
  959. }
  960. $DB->Commit();
  961. return $NS["CNT"];
  962. }
  963. public static function ReindexModule($MODULE_ID, $bFull = false)
  964. {
  965. if ($bFull)
  966. {
  967. CSearch::DeleteForReindex($MODULE_ID);
  968. }
  969. $NS = Array("CLEAR" => "N", "MODULE" => "", "ID" => "", "SESS_ID" => md5(uniqid("")));
  970. //for every who wants to be reindexed
  971. foreach (GetModuleEvents("search", "OnReindex", true) as $arEvent)
  972. {
  973. if ($arEvent["TO_MODULE_ID"] != $MODULE_ID) continue;
  974. $oCallBack = new CSearchCallback;
  975. $oCallBack->MODULE = $arEvent["TO_MODULE_ID"];
  976. $oCallBack->CNT = &$NS["CNT"];
  977. $oCallBack->SESS_ID = $NS["SESS_ID"];
  978. $r = &$oCallBack;
  979. $arResult = ExecuteModuleEventEx($arEvent, array($NS, $r, "Index"));
  980. if (is_array($arResult)) //old way
  981. {
  982. foreach ($arResult as $arFields)
  983. {
  984. $ID = $arFields["ID"];
  985. if ($ID <> '')
  986. {
  987. unset($arFields["ID"]);
  988. $NS["CNT"]++;
  989. CSearch::Index($arEvent["TO_MODULE_ID"], $ID, $arFields, false, $NS["SESS_ID"]);
  990. }
  991. }
  992. }
  993. else //new way
  994. {
  995. return Array("MODULE" => $arEvent["TO_MODULE_ID"], "CNT" => $oCallBack->CNT, "ID" => $arResult, "CLEAR" => $NS["CLEAR"], "SESS_ID" => $NS["SESS_ID"]);
  996. }
  997. }
  998. if (!$bFull)
  999. {
  1000. CSearch::DeleteOld($NS["SESS_ID"], $MODULE_ID, $NS["SITE_ID"]);
  1001. }
  1002. }
  1003. public static function GetIndex($MODULE_ID, $ITEM_ID)
  1004. {
  1005. $DB = CDatabase::GetModuleConnection('search');
  1006. $rs = $DB->Query("select * from b_search_content where MODULE_ID = '".$DB->ForSql($MODULE_ID)."' and ITEM_ID = '".$DB->ForSql($ITEM_ID)."'");
  1007. $arFields = $rs->Fetch();
  1008. if (!$arFields)
  1009. {
  1010. return false;
  1011. }
  1012. $arFields["SITE_ID"] = array();
  1013. $rs = $DB->Query("select * from b_search_content_site where SEARCH_CONTENT_ID = ".$DB->ForSql($arFields["ID"]));
  1014. while ($ar = $rs->Fetch())
  1015. {
  1016. $arFields["SITE_ID"][$ar["SITE_ID"]] = $ar["URL"];
  1017. }
  1018. $arFields["PERMISSIONS"] = array();
  1019. $rs = $DB->Query("select * from b_search_content_right where SEARCH_CONTENT_ID = ".$DB->ForSql($arFields["ID"]));
  1020. while ($ar = $rs->Fetch())
  1021. {
  1022. $arFields["PERMISSIONS"][] = $ar["GROUP_CODE"];
  1023. }
  1024. $arFields["PARAMS"] = array();
  1025. $rs = $DB->Query("select * from b_search_content_param where SEARCH_CONTENT_ID = ".$DB->ForSql($arFields["ID"]));
  1026. while ($ar = $rs->Fetch())
  1027. {
  1028. $arFields["PARAMS"][$ar["PARAM_NAME"]][] = $ar["PARAM_VALUE"];
  1029. }
  1030. return $arFields;
  1031. }
  1032. //index one item (forum message, news, etc.)
  1033. //combination of ($MODULE_ID, $ITEM_ID) is used to determine the documents
  1034. public static function Index($MODULE_ID, $ITEM_ID, $arFields, $bOverWrite = false, $SEARCH_SESS_ID = "")
  1035. {
  1036. $DB = CDatabase::GetModuleConnection('search');
  1037. $arFields["MODULE_ID"] = $MODULE_ID;
  1038. $arFields["ITEM_ID"] = $ITEM_ID;
  1039. foreach (GetModuleEvents("search", "BeforeIndex", true) as $arEvent)
  1040. {
  1041. $arEventResult = ExecuteModuleEventEx($arEvent, array($arFields));
  1042. if (is_array($arEventResult))
  1043. $arFields = $arEventResult;
  1044. }
  1045. unset($arFields["MODULE_ID"]);
  1046. unset($arFields["ITEM_ID"]);
  1047. $bTitle = array_key_exists("TITLE", $arFields);
  1048. if ($bTitle)
  1049. $arFields["TITLE"] = trim($arFields["TITLE"]);
  1050. $bBody = array_key_exists("BODY", $arFields);
  1051. if ($bBody)
  1052. $arFields["BODY"] = trim($arFields["BODY"]);
  1053. $bTags = array_key_exists("TAGS", $arFields);
  1054. if ($bTags)
  1055. $arFields["TAGS"] = trim($arFields["TAGS"]);
  1056. if (!array_key_exists("SITE_ID", $arFields) && array_key_exists("LID", $arFields))
  1057. $arFields["SITE_ID"] = $arFields["LID"];
  1058. if (array_key_exists("SITE_ID", $arFields))
  1059. {
  1060. if (!is_array($arFields["SITE_ID"]))
  1061. {
  1062. $arFields["SITE_ID"] = Array($arFields["SITE_ID"] => "");
  1063. }
  1064. else
  1065. {
  1066. $bNotAssoc = true;
  1067. $i = 0;
  1068. foreach ($arFields["SITE_ID"] as $k => $val)
  1069. {
  1070. if ("".$k != "".$i)
  1071. {
  1072. $bNotAssoc = false;
  1073. break;
  1074. }
  1075. $i++;
  1076. }
  1077. if ($bNotAssoc)
  1078. {
  1079. $x = $arFields["SITE_ID"];
  1080. $arFields["SITE_ID"] = Array();
  1081. foreach ($x as $val)
  1082. $arFields["SITE_ID"][$val] = "";
  1083. }
  1084. }
  1085. if (count($arFields["SITE_ID"]) <= 0)
  1086. return 0;
  1087. reset($arFields["SITE_ID"]);
  1088. $arFields["LID"] = current($arFields["SITE_ID"]);
  1089. $arSites = array();
  1090. foreach ($arFields["SITE_ID"] as $site => $url)
  1091. {
  1092. $arSites[] = $DB->ForSQL($site, 2);
  1093. }
  1094. $strSql = "
  1095. SELECT CR.RANK
  1096. FROM b_search_custom_rank CR
  1097. WHERE CR.SITE_ID in ('".implode("', '", $arSites)."')
  1098. AND CR.MODULE_ID='".$DB->ForSQL($MODULE_ID)."'
  1099. ".(is_set($arFields, "PARAM1")? "AND (CR.PARAM1 IS NULL OR CR.PARAM1='' OR CR.PARAM1='".$DB->ForSQL($arFields["PARAM1"])."')": "")."
  1100. ".(is_set($arFields, "PARAM2")? "AND (CR.PARAM2 IS NULL OR CR.PARAM2='' OR CR.PARAM2='".$DB->ForSQL($arFields["PARAM2"])."')": "")."
  1101. ".($ITEM_ID <> ""? "AND (CR.ITEM_ID IS NULL OR CR.ITEM_ID='' OR CR.ITEM_ID='".$DB->ForSQL($ITEM_ID)."')": "")."
  1102. ORDER BY
  1103. PARAM1 DESC, PARAM2 DESC, ITEM_ID DESC
  1104. ";
  1105. $r = $DB->Query($strSql, false, "File: ".__FILE__."<br>Line: ".__LINE__);
  1106. $arFields["CUSTOM_RANK_SQL"] = $strSql;
  1107. if ($arResult = $r->Fetch())
  1108. $arFields["CUSTOM_RANK"] = $arResult["RANK"];
  1109. }
  1110. $arGroups = array();
  1111. if (is_set($arFields, "PERMISSIONS"))
  1112. {
  1113. foreach ($arFields["PERMISSIONS"] as $group_id)
  1114. {
  1115. if (is_numeric($group_id))
  1116. $arGroups[$group_id] = "G".intval($group_id);
  1117. else
  1118. $arGroups[$group_id] = $group_id;
  1119. }
  1120. }
  1121. $strSqlSelect = "";
  1122. if ($bBody) $strSqlSelect .= ",BODY";
  1123. if ($bTitle) $strSqlSelect .= ",TITLE";
  1124. if ($bTags) $strSqlSelect .= ",TAGS";
  1125. $strSql =
  1126. "SELECT ID, MODULE_ID, ITEM_ID, ".$DB->DateToCharFunction("DATE_CHANGE")." as DATE_CHANGE
  1127. ".$strSqlSelect."
  1128. FROM b_search_content
  1129. WHERE MODULE_ID = '".$DB->ForSQL($MODULE_ID)."'
  1130. AND ITEM_ID = '".$DB->ForSQL($ITEM_ID)."' ";
  1131. $r = $DB->Query($strSql, false, "File: ".__FILE__."<br>Line: ".__LINE__);
  1132. if ($arResult = $r->Fetch())
  1133. {
  1134. $ID = $arResult["ID"];
  1135. if ($bTitle && $bBody && $arFields["BODY"] == '' && $arFields["TITLE"] == '')
  1136. {
  1137. foreach (GetModuleEvents("search", "OnBeforeIndexDelete", true) as $arEvent)
  1138. ExecuteModuleEventEx($arEvent, array("SEARCH_CONTENT_ID = ".$ID));
  1139. CSearchTags::CleanCache("", $ID);
  1140. CSearch::CleanFreqCache($ID);
  1141. $DB->Query("DELETE FROM b_search_content_param WHERE SEARCH_CONTENT_ID = ".$ID, false, "File: ".__FILE__."<br>Line: ".__LINE__);
  1142. $DB->Query("DELETE FROM b_search_content_right WHERE SEARCH_CONTENT_ID = ".$ID, false, "File: ".__FILE__."<br>Line: ".__LINE__);
  1143. $DB->Query("DELETE FROM b_search_content_site WHERE SEARCH_CONTENT_ID = ".$ID, false, "File: ".__FILE__."<br>Line: ".__LINE__);
  1144. $DB->Query("DELETE FROM b_search_content_title WHERE SEARCH_CONTENT_ID = ".$ID, false, "File: ".__FILE__."<br>Line: ".__LINE__);
  1145. $DB->Query("DELETE FROM b_search_tags WHERE SEARCH_CONTENT_ID = ".$ID, false, "File: ".__FILE__."<br>Line: ".__LINE__);
  1146. CSearchFullText::getInstance()->deleteById($ID);
  1147. $DB->Query("DELETE FROM b_search_content WHERE ID = ".$ID, false, "File: ".__FILE__."<br>Line: ".__LINE__);
  1148. return 0;
  1149. }
  1150. if (is_set($arFields, "PARAMS"))
  1151. CAllSearch::SetContentItemParams($ID, $arFields["PARAMS"]);
  1152. if (count($arGroups) > 0)
  1153. CAllSearch::SetContentItemGroups($ID, $arGroups);
  1154. if (is_set($arFields, "SITE_ID"))
  1155. {
  1156. CSearch::UpdateSite($ID, $arFields["SITE_ID"]);
  1157. }
  1158. if (array_key_exists("LAST_MODIFIED", $arFields))
  1159. $arFields["~DATE_CHANGE"] = $arFields["DATE_CHANGE"] = $DATE_CHANGE = $arFields["LAST_MODIFIED"];
  1160. elseif (array_key_exists("DATE_CHANGE", $arFields))
  1161. $arFields["~DATE_CHANGE"] = $arFields["DATE_CHANGE"] = $DATE_CHANGE = $DB->FormatDate($arFields["DATE_CHANGE"], "DD.MM.YYYY HH:MI:SS", CLang::GetDateFormat());
  1162. else
  1163. $DATE_CHANGE = '';
  1164. if (!$bOverWrite && $DATE_CHANGE == $arResult["DATE_CHANGE"])
  1165. {
  1166. if ($SEARCH_SESS_ID <> '')
  1167. $DB->Query("UPDATE b_search_content SET UPD='".$DB->ForSql($SEARCH_SESS_ID)."' WHERE ID = ".$ID, false, "File: ".__FILE__."<br>Line: ".__LINE__);
  1168. //$DB->Commit();
  1169. return $ID;
  1170. }
  1171. unset($arFields["MODULE_ID"]);
  1172. unset($arFields["ITEM_ID"]);
  1173. if ($bBody || $bTitle || $bTags)
  1174. {
  1175. if (array_key_exists("INDEX_TITLE", $arFields) && $arFields["INDEX_TITLE"] === false)
  1176. {
  1177. $content = "";
  1178. }
  1179. else
  1180. {
  1181. if ($bTitle)
  1182. $content = $arFields["TITLE"]."\r\n";
  1183. else
  1184. $content = $arResult["TITLE"]."\r\n";
  1185. }
  1186. if ($bBody)
  1187. $content .= $arFields["BODY"]."\r\n";
  1188. else
  1189. $content .= $arResult["BODY"]."\r\n";
  1190. if ($bTags)
  1191. $content .= $arFields["TAGS"];
  1192. else
  1193. $content .= $arResult["TAGS"];
  1194. $content = preg_replace_callback("/&#(\\d+);/", array("CSearch", "chr"), $content);
  1195. $arFields["SEARCHABLE_CONTENT"] = CSearch::KillEntities(ToUpper($content));
  1196. }
  1197. if ($SEARCH_SESS_ID <> '')
  1198. $arFields["UPD"] = $SEARCH_SESS_ID;
  1199. if (array_key_exists("TITLE", $arFields))
  1200. {
  1201. $DB->Query("DELETE FROM b_search_content_title WHERE SEARCH_CONTENT_ID = ".$ID, false, "File: ".__FILE__."<br>Line: ".__LINE__);
  1202. if (
  1203. !array_key_exists("INDEX_TITLE", $arFields)
  1204. || $arFields["INDEX_TITLE"] !== false
  1205. )
  1206. CSearch::IndexTitle($arFields["SITE_ID"], $ID, $arFields["TITLE"]);
  1207. }
  1208. if ($bTags && ($arResult["TAGS"] != $arFields["TAGS"]))
  1209. {
  1210. CSearchTags::CleanCache("", $ID);
  1211. $DB->Query("DELETE FROM b_search_tags WHERE SEARCH_CONTENT_ID = ".$ID, false, "File: ".__FILE__."<br>Line: ".__LINE__);
  1212. CSearch::TagsIndex($arFields["SITE_ID"], $ID, $arFields["TAGS"]);
  1213. }
  1214. foreach (GetModuleEvents("search", "OnBeforeIndexUpdate", true) as $arEvent)
  1215. ExecuteModuleEventEx($arEvent, array($ID, $arFields));
  1216. CSearch::Update($ID, $arFields);
  1217. $arFields["MODULE_ID"] = $arResult['MODULE_ID'];
  1218. $arFields["ITEM_ID"] = $arResult['ITEM_ID'];
  1219. CSearchFullText::getInstance()->replace($ID, $arFields);
  1220. }
  1221. else
  1222. {
  1223. if ($bTitle && $bBody && $arFields["BODY"] == '' && $arFields["TITLE"] == '')
  1224. {
  1225. //$DB->Commit();
  1226. return 0;
  1227. }
  1228. $arFields["MODULE_ID"] = $MODULE_ID;
  1229. $arFields["ITEM_ID"] = $ITEM_ID;
  1230. if (array_key_exists("INDEX_TITLE", $arFields) && $arFields["INDEX_TITLE"] === false)
  1231. $content = $arFields["BODY"]."\r\n".$arFields["TAGS"];
  1232. else
  1233. $content = $arFields["TITLE"]."\r\n".$arFields["BODY"]."\r\n".$arFields["TAGS"];
  1234. $content = preg_replace_callback("/&#(\\d+);/", array("CSearch", "chr"), $content);
  1235. $arFields["SEARCHABLE_CONTENT"] = CSearch::KillEntities(ToUpper($content));
  1236. if ($SEARCH_SESS_ID != "")
  1237. $arFields["UPD"] = $SEARCH_SESS_ID;
  1238. $ID = CSearch::Add($arFields);
  1239. //We failed to add this record to the search index
  1240. if ($ID === false)
  1241. {
  1242. //Check if item was added
  1243. $strSql = "SELECT ID FROM b_search_content WHERE MODULE_ID = '".$DB->ForSQL($MODULE_ID)."' AND ITEM_ID = '".$DB->ForSQL($ITEM_ID)."' ";
  1244. $rs = $DB->Query($strSql, false, "File: ".__FILE__."<br>Line: ".__LINE__);
  1245. $ar = $rs->Fetch();
  1246. if ($ar)
  1247. return $ar["ID"];
  1248. else
  1249. return $ID;
  1250. }
  1251. CSearchFullText::getInstance()->replace($ID, $arFields);
  1252. foreach (GetModuleEvents("search", "OnAfterIndexAdd", true) as $arEvent)
  1253. ExecuteModuleEventEx($arEvent, array($ID, $arFields));
  1254. if (is_set($arFields, "PARAMS"))
  1255. CAllSearch::SetContentItemParams($ID, $arFields["PARAMS"]);
  1256. CAllSearch::SetContentItemGroups($ID, $arGroups);
  1257. CSearch::UpdateSite($ID, $arFields["SITE_ID"]);
  1258. if (
  1259. !array_key_exists("INDEX_TITLE", $arFields)
  1260. || $arFields["INDEX_TITLE"] !== false
  1261. )
  1262. CSearch::IndexTitle($arFields["SITE_ID"], $ID, $arFields["TITLE"]);
  1263. CSearch::TagsIndex($arFields["SITE_ID"], $ID, $arFields["TAGS"]);
  1264. }
  1265. //$DB->Commit();
  1266. return $ID;
  1267. }
  1268. public static function KillEntities($str)
  1269. {
  1270. static $arAllEntities = array(
  1271. 'UMLYA' => ARRAY(
  1272. '&IQUEST;', '&AGRAVE;', '&AACUTE;', '&ACIRC;', '&ATILDE;',
  1273. '&AUML;', '&ARING;', '&AELIG;', '&CCEDIL;', '&EGRAVE;',
  1274. '&EACUTE;', '&ECIRC;', '&EUML;', '&IGRAVE;', '&IACUTE;',
  1275. '&ICIRC;', '&IUML;', '&ETH;', '&NTILDE;', '&OGRAVE;',
  1276. '&OACUTE;', '&OCIRC;', '&OTILDE;', '&OUML;', '&TIMES;',
  1277. '&OSLASH;', '&UGRAVE;', '&UACUTE;', '&UCIRC;', '&UUML;',
  1278. '&YACUTE;', '&THORN;', '&SZLIG;', '&AGRAVE;', '&AACUTE;',
  1279. '&ACIRC;', '&ATILDE;', '&AUML;', '&ARING;', '&AELIG;',
  1280. '&CCEDIL;', '&EGRAVE;', '&EACUTE;', '&ECIRC;', '&EUML;',
  1281. '&IGRAVE;', '&IACUTE;', '&ICIRC;', '&IUML;', '&ETH;',
  1282. '&NTILDE;', '&OGRAVE;', '&OACUTE;', '&OCIRC;', '&OTILDE;',
  1283. '&OUML;', '&DIVIDE;', '&OSLASH;', '&UGRAVE;', '&UACUTE;',
  1284. '&UCIRC;', '&UUML;', '&YACUTE;', '&THORN;', '&YUML;',
  1285. '&OELIG;', '&OELIG;', '&SCARON;', '&SCARON;', '&YUML;',
  1286. ),
  1287. 'GREEK' => ARRAY(
  1288. '&ALPHA;', '&BETA;', '&GAMMA;', '&DELTA;', '&EPSILON;',
  1289. '&ZETA;', '&ETA;', '&THETA;', '&IOTA;', '&KAPPA;',
  1290. '&LAMBDA;', '&MU;', '&NU;', '&XI;', '&OMICRON;',
  1291. '&PI;', '&RHO;', '&SIGMA;', '&TAU;', '&UPSILON;',
  1292. '&PHI;', '&CHI;', '&PSI;', '&OMEGA;', '&ALPHA;',
  1293. '&BETA;', '&GAMMA;', '&DELTA;', '&EPSILON;', '&ZETA;',
  1294. '&ETA;', '&THETA;', '&IOTA;', '&KAPPA;', '&LAMBDA;',
  1295. '&MU;', '&NU;', '&XI;', '&OMICRON;', '&PI;',
  1296. '&RHO;', '&SIGMAF;', '&SIGMA;', '&TAU;', '&UPSILON;',
  1297. '&PHI;', '&CHI;', '&PSI;', '&OMEGA;', '&THETASYM;',
  1298. '&UPSIH;', '&PIV;',
  1299. ),
  1300. 'OTHER' => ARRAY(
  1301. '&IEXCL;', '&CENT;', '&POUND;', '&CURREN;', '&YEN;',
  1302. '&BRVBAR;', '&SECT;', '&UML;', '&COPY;', '&ORDF;',
  1303. '&LAQUO;', '&NOT;', '&REG;', '&MACR;', '&DEG;',
  1304. '&PLUSMN;', '&SUP2;', '&SUP3;', '&ACUTE;', '&MICRO;',
  1305. '&PARA;', '&MIDDOT;', '&CEDIL;', '&SUP1;', '&ORDM;',
  1306. '&RAQUO;', '&FRAC14;', '&FRAC12;', '&FRAC34;', '&CIRC;',
  1307. '&TILDE;', '&ENSP;', '&EMSP;', '&THINSP;', '&ZWNJ;',
  1308. '&ZWJ;', '&LRM;', '&RLM;', '&NDASH;', '&MDASH;',
  1309. '&LSQUO;', '&RSQUO;', '&SBQUO;', '&LDQUO;', '&RDQUO;',
  1310. '&BDQUO;', '&DAGGER;', '&DAGGER;', '&PERMIL;', '&LSAQUO;',
  1311. '&RSAQUO;', '&EURO;', '&BULL;', '&HELLIP;', '&PRIME;',
  1312. '&PRIME;', '&OLINE;', '&FRASL;', '&WEIERP;', '&IMAGE;',
  1313. '&REAL;', '&TRADE;', '&ALEFSYM;', '&LARR;', '&UARR;',
  1314. '&RARR;', '&DARR;', '&HARR;', '&CRARR;', '&LARR;',
  1315. '&UARR;', '&RARR;', '&DARR;', '&HARR;', '&FORALL;',
  1316. '&PART;', '&EXIST;', '&EMPTY;', '&NABLA;', '&ISIN;',
  1317. '&NOTIN;', '&NI;', '&PROD;', '&SUM;', '&MINUS;',
  1318. '&LOWAST;', '&RADIC;', '&PROP;', '&INFIN;', '&ANG;',
  1319. '&AND;', '&OR;', '&CAP;', '&CUP;', '&INT;',
  1320. '&THERE4;', '&SIM;', '&CONG;', '&ASYMP;', '&NE;',
  1321. '&EQUIV;', '&LE;', '&GE;', '&SUB;', '&SUP;',
  1322. '&NSUB;', '&SUBE;', '&SUPE;', '&OPLUS;', '&OTIMES;',
  1323. '&PERP;', '&SDOT;', '&LCEIL;', '&RCEIL;', '&LFLOOR;',
  1324. '&RFLOOR;', '&LANG;', '&RANG;', '&LOZ;', '&SPADES;',
  1325. '&CLUBS;', '&HEARTS;', '&DIAMS;',
  1326. ),
  1327. );
  1328. static $pregEntities = false;
  1329. if (!$pregEntities)
  1330. {
  1331. $pregEntities = array();
  1332. foreach ($arAllEntities as $key => $entities)
  1333. {
  1334. $pregEntities[$key] = implode("|", $entities);
  1335. }
  1336. }
  1337. return preg_replace("/(".implode("|", $pregEntities).")/i", "", $str);
  1338. }
  1339. public static function ReindexFile($path, $SEARCH_SESS_ID = "")
  1340. {
  1341. global $APPLICATION;
  1342. $io = CBXVirtualIo::GetInstance();
  1343. $DB = CDatabase::GetModuleConnection('search');
  1344. if (!is_array($path))
  1345. return 0;
  1346. $file_doc_root = CSite::GetSiteDocRoot($path[0]);
  1347. $file_rel_path = $path[1];
  1348. $file_abs_path = preg_replace("#[\\\\\\/]+#", "/", $file_doc_root."/".$file_rel_path);
  1349. $f = $io->GetFile($file_abs_path);
  1350. if (!$f->IsExists() || !$f->IsReadable())
  1351. return 0;
  1352. if (!CSearch::CheckPath($file_rel_path))
  1353. return 0;
  1354. $max_file_size = COption::GetOptionInt("search", "max_file_size", 0);
  1355. if (
  1356. $max_file_size > 0
  1357. && $f->GetFileSize() > ($max_file_size * 1024)
  1358. )
  1359. return 0;
  1360. $file_site = "";
  1361. $rsSites = CSite::GetList("lendir", "desc");
  1362. while ($arSite = $rsSites->Fetch())
  1363. {
  1364. $site_path = preg_replace("#[\\\\\\/]+#", "/", $arSite["ABS_DOC_ROOT"]."/".$arSite["DIR"]."/");
  1365. if (mb_strpos($file_abs_path, $site_path) === 0)
  1366. {
  1367. $file_site = $arSite["ID"];
  1368. break;
  1369. }
  1370. }
  1371. if ($file_site == "")
  1372. return 0;
  1373. $item_id = $file_site."|".$file_rel_path;
  1374. if (mb_strlen($item_id) > 255)
  1375. return 0;
  1376. if ($SEARCH_SESS_ID <> '')
  1377. {
  1378. $DATE_CHANGE = $DB->CharToDateFunction(
  1379. FormatDate(
  1380. $DB->DateFormatToPHP(CLang::GetDateFormat("FULL")), $f->GetModificationTime() + CTimeZone::GetOffset()
  1381. )
  1382. );
  1383. $strSql = "
  1384. SELECT ID
  1385. FROM b_search_content
  1386. WHERE MODULE_ID = 'main'
  1387. AND ITEM_ID = '".$DB->ForSQL($item_id)."'
  1388. AND DATE_CHANGE = ".$DATE_CHANGE."
  1389. ";
  1390. $r = $DB->Query($strSql, false, "File: ".__FILE__."<br>Line: ".__LINE__);
  1391. if ($arR = $r->Fetch())
  1392. {
  1393. $strSql = "UPDATE b_search_content SET UPD='".$DB->ForSQL($SEARCH_SESS_ID)."' WHERE ID = ".$arR["ID"];
  1394. $DB->Query($strSql, false, "File: ".__FILE__."<br>Line: ".__LINE__);
  1395. return $arR["ID"];
  1396. }
  1397. }
  1398. $arrFile = false;
  1399. foreach (GetModuleEvents("search", "OnSearchGetFileContent", true) as $arEvent)
  1400. {
  1401. if ($arrFile = ExecuteModuleEventEx($arEvent, array($file_abs_path, $SEARCH_SESS_ID)))
  1402. break;
  1403. }
  1404. if (!is_array($arrFile))
  1405. {
  1406. $sFile = $APPLICATION->GetFileContent($file_abs_path);
  1407. $sHeadEndPos = mb_strpos($sFile, "</head>");
  1408. if ($sHeadEndPos === false)
  1409. $sHeadEndPos = mb_strpos($sFile, "</HEAD>");
  1410. if ($sHeadEndPos !== false)
  1411. {
  1412. //html header detected try to get document charset
  1413. $arMetaMatch = array();
  1414. if (preg_match("/<(meta)\\s+([^>]*)(content)\\s*=\\s*(['\"]).*?(charset)\\s*=\\s*(.*?)(\\4)/is", mb_substr($sFile, 0, $sHeadEndPos), $arMetaMatch))
  1415. {
  1416. $doc_charset = $arMetaMatch[6];
  1417. if (defined("BX_UTF"))
  1418. {
  1419. if (mb_strtoupper($doc_charset) != "UTF-8")
  1420. $sFile = $APPLICATION->ConvertCharset($sFile, $doc_charset, "UTF-8");
  1421. }
  1422. }
  1423. }
  1424. $arrFile = ParseFileContent($sFile);
  1425. }
  1426. $title = CSearch::KillTags(trim($arrFile["TITLE"]));
  1427. if ($title == '')
  1428. return 0;
  1429. //strip out all the tags
  1430. $filesrc = CSearch::KillTags($arrFile["CONTENT"]);
  1431. $arGroups = CSearch::GetGroupCached();
  1432. $arGPerm = Array();
  1433. foreach ($arGroups as $group_id)
  1434. {
  1435. $p = $APPLICATION->GetFileAccessPermission(Array($file_site, $file_rel_path), Array($group_id));
  1436. if ($p >= "R")
  1437. {
  1438. $arGPerm[] = $group_id;
  1439. if ($group_id == 2) break;
  1440. }
  1441. }
  1442. $tags = COption::GetOptionString("search", "page_tag_property");
  1443. //save to database
  1444. $ID = CSearch::Index("main", $item_id,
  1445. Array(
  1446. "SITE_ID" => $file_site,
  1447. "DATE_CHANGE" => date("d.m.Y H:i:s", $f->GetModificationTime() + 1),
  1448. "PARAM1" => "",
  1449. "PARAM2" => "",
  1450. "URL" => $file_rel_path,
  1451. "PERMISSIONS" => $arGPerm,
  1452. "TITLE" => $title,
  1453. "BODY" => $filesrc,
  1454. "TAGS" => array_key_exists($tags, $arrFile["PROPERTIES"])? $arrFile["PROPERTIES"][$tags]: "",
  1455. ), false, $SEARCH_SESS_ID
  1456. );
  1457. return $ID;
  1458. }
  1459. public static function RecurseIndex($path = Array(), $max_execution_time = 0, &$NS)
  1460. {
  1461. if (!is_array($path))
  1462. return 0;
  1463. $site = $path[0];
  1464. $path = $path[1];
  1465. $DOC_ROOT = CSite::GetSiteDocRoot($site);
  1466. $abs_path = $DOC_ROOT.$path;
  1467. $io = CBXVirtualIo::GetInstance();
  1468. if (!$io->DirectoryExists($abs_path))
  1469. return 0;
  1470. $f = $io->GetFile($abs_path);
  1471. if (!$f->IsReadable())
  1472. return 0;
  1473. $d = $io->GetDirectory($abs_path);
  1474. foreach ($d->GetChildren() as $dir_entry)
  1475. {
  1476. $path_file = $path."/".$dir_entry->GetName();
  1477. if ($dir_entry->IsDirectory())
  1478. {
  1479. if ($path_file == "/bitrix")
  1480. continue;
  1481. //this is not first step and we had stopped here, so go on to reindex
  1482. if (
  1483. $max_execution_time <= 0
  1484. || $NS["MODULE"] == ''
  1485. || (
  1486. $NS["M