BxDolSearch.php 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299
  1. <?php defined('BX_DOL') or die('hack attempt');
  2. /**
  3. * Copyright (c) UNA, Inc - https://una.io
  4. * MIT License - https://opensource.org/licenses/MIT
  5. *
  6. * @defgroup UnaCore UNA Core
  7. * @{
  8. */
  9. define('BX_DOL_SEARCH_RESULTS_PER_PAGE_DEFAULT', 10);
  10. define('BX_DOL_SEARCH_KEYWORD_PAGE', 'site-search-page');
  11. /**
  12. * Search class for different content.
  13. *
  14. * Allows present content in main search area for all modules.
  15. *
  16. * To add taking part content from your module to search area you need add a record to `sys_objects_search` table:
  17. * @code
  18. * ID - autoincremented id for internal usage
  19. * ObjectName - your unique module name, with vendor prefix, lowercase and spaces are underscored
  20. * Title - language key for module's choicer in global search form
  21. * ClassName - class name which is responsible for search process ad showing result for found results
  22. * ClassPath - file where your ClassName is stored (if class is stored in template folder then you can use key {tmpl})
  23. * @endcode
  24. *
  25. * For using this class you should have unit of BxDolSearchResult class (see description below) with method processing which is responsible for processing of search request.
  26. *
  27. * Example of usage can be seen in the BoonEx modules, for example: Posts
  28. *
  29. * Memberships/ACL:
  30. * Doesn't depend on user's membership.
  31. *
  32. * Alerts:
  33. * no alerts available
  34. *
  35. */
  36. class BxDolSearch extends BxDol
  37. {
  38. protected $_bIsApi;
  39. protected $aClasses = array(); ///< array of all search classes
  40. protected $aChoice = array(); ///< array of current search classes which were choosen in search area
  41. protected $_bRawProcessing = false; ///< display search results without design box and paginate
  42. protected $_bDataProcessing = false; ///< return result as array of data
  43. protected $_bLiveSearch = false;
  44. protected $_sMetaType = '';
  45. protected $_sCategoryObject = ''; ///< by default = object from sys_objects_category table, for multicategories set to 'multi' string
  46. protected $_aCustomSearchCondition = array();
  47. protected $_aCustomCurrentCondition = array();
  48. protected $_sUnitTemplate = '';
  49. /**
  50. * Constructor
  51. * @param array $aChoice - array of choosen classes (will take a part only existing in `sys_objects_search` table)
  52. */
  53. public function __construct ($aChoice = '')
  54. {
  55. parent::__construct();
  56. $this->_bIsApi = bx_is_api();
  57. $this->aClasses = BxDolDb::getInstance()->fromCache('sys_objects_search', 'getAllWithKey',
  58. 'SELECT `ID` as `id`,
  59. `Title` as `title`,
  60. `ClassName` as `class`,
  61. `ClassPath` as `file`,
  62. `ObjectName`,
  63. `GlobalSearch`
  64. FROM `sys_objects_search`
  65. ORDER BY `Order` ASC', 'ObjectName'
  66. );
  67. if (is_array($aChoice) && !empty($aChoice)) {
  68. foreach ($aChoice as $sValue) {
  69. if (isset($this->aClasses[$sValue]))
  70. $this->aChoice[$sValue] = $this->aClasses[$sValue];
  71. }
  72. } else {
  73. $this->aChoice = $this->aClasses;
  74. }
  75. }
  76. /**
  77. * create units for all classes and calling their processing methods
  78. */
  79. public function response ()
  80. {
  81. $sCode = $this->_bDataProcessing ? array() : '';
  82. $bSingle = count($this->aChoice) == 1;
  83. foreach($this->aChoice as $sKey => $aValue) {
  84. if(!$this->_sMetaType && !$aValue['GlobalSearch'])
  85. continue;
  86. $sClassName = 'BxTemplSearchResult';
  87. if(!empty($aValue['class'])) {
  88. $sClassName = $aValue['class'];
  89. if(!empty($aValue['file']))
  90. require_once(BX_DIRECTORY_PATH_ROOT . $aValue['file']);
  91. }
  92. $oEx = new $sClassName();
  93. if($this->_sMetaType && !$oEx->isMetaEnabled($this->_sMetaType))
  94. continue;
  95. $oEx->setId($aValue['id']);
  96. $oEx->setLiveSearch($this->_bLiveSearch);
  97. $oEx->setMetaType($this->_sMetaType);
  98. $oEx->setCategoryObject($this->_sCategoryObject);
  99. $oEx->setCenterContentUnitSelector(false);
  100. $oEx->setSingleSearch($bSingle);
  101. $oEx->setCustomSearchCondition($this->_aCustomSearchCondition);
  102. $oEx->aCurrent = array_merge_recursive($oEx->aCurrent, $this->_aCustomCurrentCondition);
  103. if($this->_sUnitTemplate)
  104. $oEx->setUnitTemplate($this->_sUnitTemplate);
  105. if($this->_bDataProcessing) {
  106. if($this->_bIsApi) {
  107. if($bSingle)
  108. $sCode = $oEx->decodeDataAPI($oEx->getSearchData());
  109. else
  110. $sCode[$sKey] = $oEx->getSearchQuery($sKey);
  111. }
  112. else
  113. $sCode[$sKey] = $oEx->getSearchData();
  114. }
  115. else
  116. $sCode .= $this->_bRawProcessing ? $oEx->processingRaw() : $oEx->processing();
  117. }
  118. if($this->_bIsApi && !$bSingle && is_array($sCode)) {
  119. $bExtendedUnits = getParam('sys_api_extended_units') == 'on';
  120. $aQueries = [];
  121. foreach($sCode as $aQuery)
  122. if(!empty($aQuery['query']))
  123. $aQueries[] = $aQuery['query'];
  124. $aItems = BxDolDb::getInstance()->getAll('(' . implode(') UNION (', $aQueries) . ') ORDER BY `added` DESC ' . current($sCode)['limit']);
  125. $sCode = [];
  126. foreach($aItems as $aItem)
  127. if(($oContentInfo = BxDolContentInfo::getObjectInstance($aItem['content_info'])) !== false)
  128. $sCode[] = $oContentInfo->getContentInfoAPI($aItem['id'], $bExtendedUnits);
  129. }
  130. return $sCode;
  131. }
  132. public function getEmptyResult ()
  133. {
  134. $sKey = _t('_Empty');
  135. return DesignBoxContent($sKey, MsgBox($sKey), BX_DB_PADDING_DEF);
  136. }
  137. protected function getKeyTitlesPairs ()
  138. {
  139. $a = array();
  140. foreach ($this->aChoice as $sKey => $r)
  141. if ($this->_sMetaType || $r['GlobalSearch'])
  142. $a[$sKey] = _t($r['title']);
  143. return $a;
  144. }
  145. public function setLiveSearch($bLiveSearch)
  146. {
  147. $this->_bLiveSearch = $bLiveSearch;
  148. }
  149. public function setMetaType($s)
  150. {
  151. $aMetaTypes = array('location_country', 'location_country_city', 'location_country_state', 'mention', 'keyword');
  152. if (in_array($s, $aMetaTypes))
  153. $this->_sMetaType = $s;
  154. }
  155. public function setCategoryObject($s)
  156. {
  157. $this->_sCategoryObject = $s;
  158. }
  159. /**
  160. * Set custom search condition to use istead of GET/POST variables
  161. * @param $a array of params, such as 'keyword', 'state', 'city'
  162. * @return nothing
  163. */
  164. public function setCustomSearchCondition($a)
  165. {
  166. $this->_aCustomSearchCondition = $a;
  167. }
  168. /**
  169. * Set custom data for aCurrent array
  170. * @param $a array of params
  171. * @return nothing
  172. */
  173. public function setCustomCurrentCondition($a)
  174. {
  175. $this->_aCustomCurrentCondition = $a;
  176. }
  177. /**
  178. * Set custom unit template
  179. * @param $s template name
  180. * @return nothing
  181. */
  182. public function setUnitTemplate($s)
  183. {
  184. $this->_sUnitTemplate = $s;
  185. }
  186. /**
  187. * Display search results without design box and paginate
  188. */
  189. public function setRawProcessing($b)
  190. {
  191. $this->_bRawProcessing = $b;
  192. }
  193. /**
  194. * Return search result as array of data
  195. */
  196. public function setDataProcessing($b)
  197. {
  198. $this->_bDataProcessing = $b;
  199. }
  200. }
  201. /*
  202. * Search class for processing search requests and displaying search results.
  203. *
  204. * Allows present content from modules on search params or internal (via fields of class) conditions.
  205. *
  206. * Example of usage (you can see example in any of default Dolhpin's modules):
  207. *
  208. * 1. Extends your own search class from this one (or from BxBaseSearchResult or BxBaseSearchResultSharedMedia classes)
  209. * 2. Set necessary fields of class (using as example BxFilesSearch):
  210. *
  211. * @code
  212. *
  213. * // main field of search class
  214. * $this->aCurrent = array(
  215. *
  216. * // name of module
  217. * 'name' => 'bx_files',
  218. *
  219. * // language key with name of module
  220. * 'title' => '_bx_files',
  221. *
  222. * // main content table
  223. * 'table' => 'bx_files_main',
  224. *
  225. * // array of all fields which can be choosen for select in result query
  226. * 'ownFields' => array('ID', 'Title', 'Uri', 'Desc', 'Date', 'Size', 'Ext', 'Views', 'Rate', 'RateCount', 'Type'),
  227. *
  228. * // array of fields which take a part in search by keyword (global search and related words only - in other cases leave it blank)
  229. * 'searchFields' => array('Title', 'Tags', 'Desc', 'Categories'),
  230. *
  231. * // array of join tables
  232. * // 'type' - type of join
  233. * // 'table' - join table
  234. * // 'mainField' - field from main table for 'on' condition
  235. * // 'onField' - field from joining table for 'on' condition
  236. * // 'joinFields' - array of fields from joining table
  237. *
  238. * 'join' => array(
  239. * 'profile' => array(
  240. * 'type' => 'left',
  241. * 'table' => 'Profiles',
  242. * 'mainField' => 'Owner',
  243. * 'onField' => 'ID',
  244. * 'joinFields' => array('NickName'),
  245. * ),
  246. * // ...
  247. * ),
  248. *
  249. * // array of search parameters
  250. * // 'value' - value of search parameter (can be number, string or array)
  251. * // 'field' - field which will take value of search, may be lefy as blank then in search query will be pasted to WHERE conditon via operator AGAINST
  252. * // 'operator' - operator between field and value (can be 'in', 'not in', '>', '<', 'against', 'like' and '=' by default),
  253. * // 'paramName' - GET param which will keep 'value' for pagianation
  254. *
  255. * 'restriction' => array(
  256. * 'activeStatus' => array('value'=>'approved', 'field'=>'Status', 'operator'=>'=', 'paramName' => 'status'),
  257. * 'albumType' => array('value'=>'', 'field'=>'Type', 'operator'=>'=', 'paramName'=>'albumType', 'table'=>'sys_albums'),
  258. * ),
  259. *
  260. * // array of pagination
  261. * // 'perPage' - units per page
  262. * // 'start' - show search results starting from this number
  263. * // 'num' - number of element to show on current page + 1
  264. *
  265. * 'paginate' => array('perPage' => 10, 'start' => 0, 'num' => 11),
  266. *
  267. * // sort mode - by default it was last (DESC by date)
  268. * 'sorting' => 'last',
  269. *
  270. * // mode of units presentation in search result, can be 'short' or 'full' view
  271. * 'view' => 'full',
  272. *
  273. * // field of id
  274. * 'ident' => 'ID',
  275. *
  276. * // rss feed array
  277. * 'rss' => array(
  278. * 'title' => '',
  279. * 'link' => '',
  280. * 'image' => '',
  281. * 'profile' => 0,
  282. * 'fields' => array (
  283. * 'Link' => '',
  284. * 'Title' => 'Title',
  285. * 'DateTimeUTS' => 'Date',
  286. * 'Desc' => 'Desc',
  287. * 'Photo' => '',
  288. * ),
  289. * ),
  290. * );
  291. *
  292. * // array of fields renamings
  293. * $aPseud - filling in costructor by function _getPseud
  294. *
  295. * // unique identificator from `sys_objects_search` table
  296. * $id;
  297. *
  298. * @endcode
  299. *
  300. * Memberships/ACL:
  301. * Doesn't depend on user's membership.
  302. *
  303. * Alerts:
  304. * no alerts available
  305. *
  306. */
  307. class BxDolSearchResult implements iBxDolReplaceable
  308. {
  309. public $aCurrent; ///< search results configuration
  310. protected $aPseud; ///< array of fields renamings
  311. protected $id; ///< unique identificator of block on mixed search results page
  312. protected $bDisplayEmptyMsg = false; ///< display empty message instead of nothing, when no results
  313. protected $sDisplayEmptyMsgKey = ''; ///< custom empty message language key, instead of default "empty" message
  314. protected $bProcessPrivateContent = true; ///< check each item for privacy, if view isn't allowed then display private view instead
  315. protected $aPrivateConditionsIndexes = array('restriction' => array(), 'join' => array()); ///< conditions indexes for bProcessPrivateContent
  316. protected $bForceAjaxPaginate = false; ///< force ajax paginate
  317. protected $iPaginatePerPage = BX_DOL_SEARCH_RESULTS_PER_PAGE_DEFAULT; ///< default 'per page' value for paginate.
  318. protected $_bIsApi;
  319. protected $_bSingleSearch = true;
  320. protected $_bLiveSearch = false;
  321. protected $_sMetaType = '';
  322. protected $_sMode = '';
  323. protected $_aParams = [];
  324. protected $_sCategoryObject = '';
  325. protected $_aCustomSearchCondition = array();
  326. protected $_aMarkers = array (); ///< markers to replace somewhere, usually title and browse url (defined in custom class)
  327. /**
  328. * constructor
  329. * filling identificator field
  330. */
  331. function __construct ()
  332. {
  333. $this->_bIsApi = bx_is_api();
  334. if (isset($this->aPseud['id']))
  335. $this->aCurrent['ident'] = $this->aPseud['id'];
  336. }
  337. public function getId()
  338. {
  339. return $this->id;
  340. }
  341. public function setId($sId)
  342. {
  343. $this->id = $sId;
  344. }
  345. public function getModuleName()
  346. {
  347. return isset($this->aCurrent['module_name']) ? $this->aCurrent['module_name'] : '';
  348. }
  349. public function getContentInfoName()
  350. {
  351. return isset($this->aCurrent['name']) ? $this->aCurrent['name'] : '';
  352. }
  353. public function setAjaxPaginate($b = true)
  354. {
  355. $this->bForceAjaxPaginate = $b;
  356. }
  357. public function setPaginatePerPage($iPerPage)
  358. {
  359. $this->iPaginatePerPage = $iPerPage;
  360. }
  361. public function setSingleSearch($bSingleSearch)
  362. {
  363. $this->_bSingleSearch = $bSingleSearch;
  364. }
  365. public function setLiveSearch($bLiveSearch)
  366. {
  367. $this->_bLiveSearch = $bLiveSearch;
  368. }
  369. public function setMetaType($s)
  370. {
  371. $this->_sMetaType = $s;
  372. }
  373. public function isMetaEnabled($s)
  374. {
  375. if (empty($this->aCurrent['object_metatags']))
  376. return false;
  377. if (!($o = BxDolMetatags::getObjectInstance($this->aCurrent['object_metatags'])))
  378. return false;
  379. switch ($s) {
  380. case 'location_country_city':
  381. case 'location_country_state':
  382. case 'location_country':
  383. return $o->locationsIsEnabled();
  384. case 'mention':
  385. return $o->mentionsIsEnabled();
  386. case 'keyword':
  387. return $o->keywordsIsEnabled();
  388. }
  389. return false;
  390. }
  391. public function setCategoryObject($s)
  392. {
  393. $this->_sCategoryObject = $s;
  394. }
  395. public function setCustomSearchCondition($a)
  396. {
  397. $this->_aCustomSearchCondition = $a;
  398. }
  399. public function setCustomCurrentCondition($a)
  400. {
  401. $this->aCurrent = array_merge_recursive($this->aCurrent, $a);
  402. }
  403. public function setCategoriesCondition($sKeyword)
  404. {
  405. $this->aCurrent['join']['multicat'] = array(
  406. 'type' => 'INNER',
  407. 'table' => 'sys_categories2objects',
  408. 'mainField' => 'id',
  409. 'onField' => 'object_id',
  410. 'joinFields' => array(),
  411. );
  412. if (isset($this->aCurrent['tableSearch'])){
  413. $this->aCurrent['join']['multicat']['mainTable'] = $this->aCurrent['tableSearch'];
  414. }
  415. $this->aCurrent['join']['multicat2'] = array(
  416. 'type' => 'INNER',
  417. 'table' => 'sys_categories',
  418. 'mainField' => 'category_id',
  419. 'mainTable' => 'sys_categories2objects',
  420. 'onField' => 'id',
  421. 'joinFields' => array(),
  422. );
  423. $this->aCurrent['restriction']['multicat'] = array('value' => $sKeyword, 'field' => 'value', 'operator' => '=', 'table' => 'sys_categories');
  424. }
  425. /**
  426. * Display empty message if there is no content, custom empty message can be used.
  427. * @param $b - boolan value to enable or disable 'empty' message
  428. * @param $sLangKey [optional] - custom 'empty' message
  429. */
  430. public function setDisplayEmptyMsg($b, $sLangKey = '')
  431. {
  432. $this->bDisplayEmptyMsg = $b;
  433. if ($sLangKey)
  434. $this->sDisplayEmptyMsgKey = $sLangKey;
  435. }
  436. /**
  437. * Perform privacy checking for every unit
  438. * @param $b - boolan value to enable or disable privacy checking
  439. */
  440. public function setProcessPrivateContent ($b)
  441. {
  442. $this->bProcessPrivateContent = $b;
  443. if ($b) {
  444. // unset condition which was set when 'bProcessPrivateContent' was set to 'false'
  445. foreach ($this->aPrivateConditionsIndexes['restriction'] as $sKey)
  446. unset($this->aCurrent['restriction'][$sKey]);
  447. foreach ($this->aPrivateConditionsIndexes['join'] as $sKey)
  448. unset($this->aCurrent['join'][$sKey]);
  449. }
  450. }
  451. /**
  452. * Get search results without design box and paginate
  453. * @return html code
  454. */
  455. function processingRaw ()
  456. {
  457. $sCode = $this->displayResultBlock();
  458. return $this->aCurrent['paginate']['num'] > 0 ? $sCode : '';
  459. }
  460. /**
  461. * Get html box of search results (usually used in grlobal search)
  462. * @return html code
  463. */
  464. function processing ()
  465. {
  466. if (bx_is_api()) {
  467. return $this->processingAPI();
  468. }
  469. $sCode = $this->displayResultBlock();
  470. if ($this->aCurrent['paginate']['num'] > 0) {
  471. $sPaginate = $this->showPagination();
  472. $sCode = $this->displaySearchBox($sCode, $sPaginate);
  473. }
  474. else {
  475. $sCode = $this->bDisplayEmptyMsg ? $this->displaySearchBox($this->displayResultBlockEmpty()) : '';
  476. }
  477. return $sCode;
  478. }
  479. function processingAPI ()
  480. {
  481. $sModule = 'system';
  482. $sUnitType = 'content';
  483. if(!empty($this->oModule)) {
  484. $sModule = $this->oModule->getName();
  485. if(method_exists($this->oModule, 'serviceActAsProfile') && $this->oModule->serviceActAsProfile())
  486. $sUnitType = 'profile';
  487. if(method_exists($this->oModule, 'serviceIsGroupProfile') && $this->oModule->serviceIsGroupProfile())
  488. $sUnitType = 'context';
  489. }
  490. $sUnit = 'list';
  491. if(in_array($this->sUnitViewDefault, ['showcase', 'gallery']))
  492. $sUnit = 'card';
  493. $aData = defined('BX_API_PAGE') ? [] : $this->decodeDataAPI($this->getSearchData());
  494. $aParams = [
  495. 'per_page' => $this->aCurrent['paginate']['perPage'],
  496. 'start' => $this->aCurrent['paginate']['start'],
  497. 'type' => $this->_sMode,
  498. ];
  499. if (isset($this->_aParams['author'])){
  500. $aParams['author'] = $this->_aParams['author'];
  501. }
  502. if (isset($this->_aParams['category'])){
  503. $aParams['category'] = $this->_aParams['category'];
  504. }
  505. return [
  506. 'module' => $sModule,
  507. 'unit' => 'general-' . $sUnitType . '-' . $sUnit,
  508. 'request_url' => !empty($this->aCurrent['api_request_url']) ? $this->aCurrent['api_request_url'] : '/api.php?r=' . $sModule . '/browse/&params[]=',
  509. 'data' => $aData,
  510. 'params' => $aParams
  511. ];
  512. }
  513. function decodeDataAPI ($a)
  514. {
  515. return $a;
  516. }
  517. /**
  518. * Get html output of search result
  519. * @return html code
  520. */
  521. function displayResultBlock ()
  522. {
  523. $aData = $this->getSearchData();
  524. $sCode = '';
  525. if (count($aData) > 0) {
  526. $sCode = $this->addCustomParts();
  527. foreach ($aData as $iKey => $aValue) {
  528. $sCode .= $this->displaySearchUnit($aValue);
  529. }
  530. }
  531. return $sCode;
  532. }
  533. /**
  534. * Get html output of empty search result
  535. * @return html code
  536. */
  537. function displayResultBlockEmpty ()
  538. {
  539. return MsgBox(_t($this->sDisplayEmptyMsgKey ? $this->sDisplayEmptyMsgKey : '_Empty'));
  540. }
  541. /**
  542. * Add different code before html output (usually redeclared)
  543. * no return result
  544. */
  545. function addCustomParts ()
  546. {
  547. }
  548. /**
  549. * Get XML string for rss output
  550. */
  551. function rss ()
  552. {
  553. if (!isset($this->aCurrent['rss']['fields']) || !isset($this->aCurrent['rss']['link']))
  554. return '';
  555. $aData = $this->getSearchData();
  556. $f = &$this->aCurrent['rss']['fields'];
  557. if ($aData) {
  558. foreach ($aData as $k => $a) {
  559. $aData[$k][$f['Link']] = $this->getRssUnitLink ($a);
  560. if(isset($f['Image']))
  561. $aData[$k][$f['Image']] = $this->getRssUnitImage ($a, $f['Image']);
  562. }
  563. }
  564. $oRss = new BxDolRssFactory ();
  565. return $oRss->GenRssByCustomData(
  566. $aData,
  567. isset($this->aCurrent['rss']['title']) && $this->aCurrent['rss']['title'] ? $this->aCurrent['rss']['title'] : $this->aCurrent['title'],
  568. $this->aCurrent['rss']['link'],
  569. $this->aCurrent['rss']['fields'],
  570. isset($this->aCurrent['rss']['image']) ? $this->aCurrent['rss']['image'] : '',
  571. isset($this->aCurrent['rss']['profile']) ? $this->aCurrent['rss']['profile'] : 0
  572. );
  573. }
  574. /**
  575. * Output RSS XML with XML header
  576. */
  577. function outputRSS ()
  578. {
  579. header('Content-Type: text/xml; charset=UTF-8');
  580. echo $this->rss();
  581. }
  582. /**
  583. * Return rss unit link (redeclared)
  584. */
  585. function getRssUnitLink (&$a)
  586. {
  587. // override this functions to return permalink to rss unit
  588. }
  589. /**
  590. * Return rss unit image (redeclared)
  591. */
  592. function getRssUnitImage (&$a, $sField)
  593. {
  594. // override this functions to return image for rss unit
  595. }
  596. /**
  597. * Naming fields in query's body
  598. * @param string $sFieldName name of field
  599. * @param string $sTableName name of field's table
  600. * $param string $sOperator of field's calculation (like MAX)
  601. * @param boolean $bRenameMode indicator for renaming and unsetting fields from field of class $this->aPseud
  602. * return $sqlUnit sql code and unsetting elements from aPseud field
  603. */
  604. function setFieldUnit ($sFieldName, $sTableName, $sOperator = '', $bRenameMode = true)
  605. {
  606. if (!empty($sOperator))
  607. $sqlUnit = "$sOperator(`$sTableName`.`$sFieldName`)";
  608. else
  609. $sqlUnit = "`$sTableName`.`$sFieldName`";
  610. if (isset($this->aPseud) && $bRenameMode !== false) {
  611. $sKey = array_search($sFieldName, $this->aPseud);
  612. if ($sKey !== false) {
  613. $sqlUnit .= " as `$sKey`";
  614. unset($this->aPseud[$sKey]);
  615. }
  616. }
  617. return $sqlUnit . ', ';
  618. }
  619. /**
  620. * Get html code of of every search unit
  621. * @param array $aData array of every search unit
  622. * return html code
  623. */
  624. function displaySearchUnit ($aData)
  625. {
  626. }
  627. /**
  628. * Get html code of search box with search results
  629. * @param string $sCode html code of search results
  630. * $param $sPaginate html code of paginate
  631. * return html code
  632. */
  633. function displaySearchBox ($sCode, $sPaginate = '')
  634. {
  635. }
  636. /**
  637. * Get html code of pagination
  638. */
  639. function showPagination ($bAdmin = false, $bChangePage = true, $bPageReload = true)
  640. {
  641. }
  642. /**
  643. * Get array of data with search results
  644. * return array with data
  645. */
  646. function getSearchData ()
  647. {
  648. bx_alert('simple_search', 'before_get_data', 0, false, [
  649. 'object' => &$this->aCurrent,
  650. 'mode' => $this->_sMode
  651. ]);
  652. $this->aPseud = $this->_getPseud();
  653. $this->setConditionParams();
  654. $aData = $this->aCurrent['paginate']['num'] > 0 ? $this->getSearchDataByParams() : [];
  655. bx_alert('simple_search', 'get_data', 0, false, [
  656. 'object' => &$this->aCurrent,
  657. 'mode' => $this->_sMode,
  658. 'search_results' => &$aData
  659. ]);
  660. return $aData;
  661. }
  662. /**
  663. * Get query [query, limit] for search results. Is used for combined search from different sections.
  664. * @param type $aParams array with params
  665. * @return type array with query and limit
  666. */
  667. function getSearchQuery($sObject, $aParams = [])
  668. {
  669. if(!is_array($aParams))
  670. $aParams = [];
  671. $aParams = array_merge($aParams, ['for_union' => true]);
  672. $this->aPseud = [
  673. 'id' => !empty($this->aCurrent['ident']) ? $this->aCurrent['ident'] : 'id',
  674. 'added' => !empty($this->aCurrent['added']) ? $this->aCurrent['added'] : 'added'
  675. ];
  676. $this->setConditionParams();
  677. $aQuery = $this->getSearchDataByParams($aParams);
  678. bx_alert('simple_search', 'get_query', 0, false, [
  679. 'object' => &$this->aCurrent,
  680. 'mode' => $this->_sMode,
  681. 'search_object' => $sObject,
  682. 'search_query' => &$aQuery
  683. ]);
  684. return $aQuery;
  685. }
  686. /**
  687. * Get array with code for sql elements
  688. * @param $bRenameMode indicator of renmaing fields
  689. * return array with joinFields, ownFields, groupBy and join elements
  690. */
  691. function getJoins ($bRenameMode = true)
  692. {
  693. $aSql = array();
  694. // joinFields & join
  695. if (isset($this->aCurrent['join']) && is_array($this->aCurrent['join'])) {
  696. $aSql = array('join'=>'', 'ownFields'=>'', 'joinFields'=>'', 'groupBy'=>'');
  697. foreach ($this->aCurrent['join'] as $sKey => $aValue) {
  698. $sAlias = isset($aValue['table_alias']) ? $aValue['table_alias'] : $aValue['table'];
  699. $sTableAlias = isset($aValue['table_alias']) ? " AS {$aValue['table_alias']} " : '';
  700. if (is_array($aValue['joinFields'])) {
  701. foreach ($aValue['joinFields'] as $sValue) {
  702. $aSql['joinFields'] .= $this->setFieldUnit($sValue, $sAlias, isset($aValue['operator']) ? $aValue['operator'] : null, $bRenameMode);
  703. }
  704. }
  705. // group by
  706. if (isset($aValue['groupTable']))
  707. $aSql['groupBy'] = "GROUP BY `{$aValue['groupTable']}`.`{$aValue['groupField']}`, ";
  708. $sOn = isset($aValue['mainTable']) ? $aValue['mainTable'] : $this->aCurrent['table'];
  709. $aSql['join'] .= " {$aValue['type']} JOIN `{$aValue['table']}` $sTableAlias ON " . (!empty($aValue['on_sql']) ? $aValue['on_sql'] : "`{$sAlias}`.`{$aValue['onField']}`=" . trim($this->setFieldUnit($aValue['mainField'], $sOn, isset($aValue['mainFieldFunc']) ? $aValue['mainFieldFunc'] : '', false), ", "));
  710. $aSql['ownFields'] .= $this->setFieldUnit($aValue['mainField'], $sOn, '', $bRenameMode);
  711. }
  712. $aSql['joinFields'] = trim($aSql['joinFields'], ', ');
  713. $aSql['groupBy'] = trim($aSql['groupBy'], ', ');
  714. }
  715. return $aSql;
  716. }
  717. /**
  718. * Concat sql parts of query, run it and return result array
  719. * @param $aParams addon param
  720. * return $aData multivariate array
  721. */
  722. function getSearchDataByParams ($aParams = '')
  723. {
  724. $bForUnion = isset($aParams['for_union']) && $aParams['for_union'] === true;
  725. $aSql = array('ownFields'=>'', 'joinFields'=>'', 'order'=>'');
  726. // searchFields
  727. if($bForUnion) {
  728. $sTable = isset($this->aCurrent['tableSearch']) ? $this->aCurrent['tableSearch'] : $this->aCurrent['table'];
  729. $aSql['ownFields'] .= $this->setFieldUnit(!empty($this->aCurrent['ident']) ? $this->aCurrent['ident'] : 'id', $sTable);
  730. $aSql['ownFields'] .= $this->setFieldUnit(!empty($this->aCurrent['added']) ? $this->aCurrent['added'] : 'added', $sTable);
  731. $aSql['ownFields'] .= "'" . $this->getContentInfoName() . "' AS `content_info`";
  732. }
  733. else
  734. foreach ($this->aCurrent['ownFields'] as $sValue)
  735. $aSql['ownFields'] .= $this->setFieldUnit($sValue, $this->aCurrent['table']);
  736. // joinFields & join
  737. $aJoins = $this->getJoins();
  738. if (!empty($aJoins)) {
  739. if(!$bForUnion) {
  740. $aSql['ownFields'] .= $aJoins['ownFields'];
  741. $aSql['ownFields'] .= $aJoins['joinFields'];
  742. }
  743. $aSql['join'] = $aJoins['join'];
  744. $aSql['groupBy'] = $aJoins['groupBy'];
  745. }
  746. $aSql['ownFields'] = trim($aSql['ownFields'], ', ');
  747. // from
  748. $aSql['from'] = " FROM `{$this->aCurrent['table']}`";
  749. // where
  750. $aSql['where'] = $this->getRestriction();
  751. // limit
  752. $aSql['limit'] = $this->getLimit();
  753. // sorting
  754. $this->setSorting();
  755. $aSort = $this->getSorting($this->aCurrent['sorting']);
  756. foreach ($aSort as $sKey => $sValue)
  757. $aSql[$sKey] .= $sValue;
  758. // execution
  759. $sqlQuery = 'SELECT';
  760. if(isset($this->aCurrent['distinct']) && $this->aCurrent['distinct'] === true)
  761. $sqlQuery .= ' DISTINCT';
  762. $sqlQuery .= ' ' . $aSql['ownFields'];
  763. $sqlQuery .= ' ' . $aSql['from'];
  764. if (isset($aSql['join']))
  765. $sqlQuery .= ' ' . $aSql['join'];
  766. if (isset($aSql['where']))
  767. $sqlQuery .= ' ' . $aSql['where'];
  768. if (isset($aSql['groupBy']))
  769. $sqlQuery .= ' ' . $aSql['groupBy'];
  770. if($bForUnion)
  771. return [
  772. 'query' => $sqlQuery,
  773. 'limit' => $aSql['limit']
  774. ];
  775. if (isset($aSql['order']))
  776. $sqlQuery .= ' ' . $aSql['order'];
  777. if (isset($aSql['limit']))
  778. $sqlQuery .= ' ' . $aSql['limit'];
  779. // echoDbg($sqlQuery);
  780. $aRes = BxDolDb::getInstance()->getAll($sqlQuery);
  781. return $aRes;
  782. }
  783. /**
  784. * Set class fields condition params and paginate array
  785. */
  786. function setConditionParams()
  787. {
  788. $this->aCurrent['paginate']['num'] = 0;
  789. // keyword
  790. $sKeyword = bx_process_input(isset($this->_aCustomSearchCondition['keyword']) ? $this->_aCustomSearchCondition['keyword'] : bx_get('keyword'));
  791. if ($this->_bLiveSearch && empty($sKeyword))
  792. return;
  793. if ($sKeyword !== false) {
  794. if (substr($sKeyword, 0, 1) == '@') {
  795. $sModule = $this->aCurrent['module_name'];
  796. $sMethod = 'act_as_profile';
  797. if(!bx_is_srv($sModule, $sMethod) || !bx_srv($sModule, $sMethod))
  798. return;
  799. $sKeyword = substr($sKeyword, 1);
  800. }
  801. $this->aCurrent['restriction']['keyword'] = array(
  802. 'value' => $sKeyword,
  803. 'field' => '',
  804. 'operator' => 'against'
  805. );
  806. // for search results we need to show all items, not only public content
  807. $this->setProcessPrivateContent(true);
  808. }
  809. // owner
  810. if (isset($_GET['ownerName'])) {
  811. $sName = bx_process_input($_GET['ownerName']);
  812. $iUser = (int)BxDolProfileQuery::getInstance()->getIdByNickname($sName);
  813. BxDolMenu::getInstance()->setCurrentProfileID($iUser);
  814. } elseif (isset($_GET['userID']))
  815. $iUser = bx_process_input($_GET['userID'], BX_DATA_INT);
  816. if (!empty($iUser))
  817. $this->aCurrent['restriction']['owner']['value'] = $iUser;
  818. // meta info
  819. if ($this->_sMetaType && !empty($this->aCurrent['object_metatags'])) {
  820. $o = BxDolMetatags::getObjectInstance($this->aCurrent['object_metatags']);
  821. if ($o) {
  822. unset($this->aCurrent['restriction']['keyword']);
  823. switch ($this->_sMetaType) {
  824. case 'location_country':
  825. $o->locationsSetSearchCondition($this, $sKeyword);
  826. break;
  827. case 'location_country_state':
  828. $o->locationsSetSearchCondition($this, $sKeyword, bx_process_input(isset($this->_aCustomSearchCondition['state']) ? $this->_aCustomSearchCondition['state'] : bx_get('state')));
  829. break;
  830. case 'location_country_city':
  831. $o->locationsSetSearchCondition($this, $sKeyword, bx_process_input(isset($this->_aCustomSearchCondition['state']) ? $this->_aCustomSearchCondition['state'] : bx_get('state')), bx_process_input(isset($this->_aCustomSearchCondition['city']) ? $this->_aCustomSearchCondition['city'] : bx_get('city')));
  832. break;
  833. case 'location_state':
  834. $o->locationsSetSearchCondition($this, false, $sKeyword);
  835. break;
  836. case 'location_city':
  837. $o->locationsSetSearchCondition($this, false, false, $sKeyword);
  838. break;
  839. case 'mention':
  840. $oCmts = !empty($this->sModuleObjectComments) ? BxDolCmts::getObjectInstance($this->sModuleObjectComments, 0, false) : false;
  841. $o->mentionsSetSearchCondition($this, $sKeyword, $oCmts ? $oCmts->getSystemId() : 0);
  842. break;
  843. case 'keyword':
  844. $oCmts = !empty($this->sModuleObjectComments) ? BxDolCmts::getObjectInstance($this->sModuleObjectComments, 0, false) : false;
  845. $o->keywordsSetSearchCondition($this, $sKeyword, $oCmts ? $oCmts->getSystemId() : 0);
  846. break;
  847. }
  848. }
  849. }
  850. // category
  851. if ($this->_sCategoryObject){
  852. if(($o = BxDolCategory::getObjectInstance($this->_sCategoryObject)) && $this->_bSingleSearch) {
  853. if ($this->aCurrent['name'] == $o->getSearchObject()) {
  854. unset($this->aCurrent['restriction']['keyword']);
  855. $o->setSearchCondition($this, $sKeyword);
  856. }
  857. }
  858. if ($this->_sCategoryObject == 'multi'){
  859. unset($this->aCurrent['restriction']['keyword']);
  860. $this->setCategoriesCondition($sKeyword);
  861. }
  862. }
  863. $this->setPaginate();
  864. $iNum = $this->getNum();
  865. if ($iNum > 0)
  866. $this->aCurrent['paginate']['num'] = $iNum;
  867. }
  868. /**
  869. * Check number of records on current page
  870. * return number of records on current page + 1
  871. */
  872. function getNum ()
  873. {
  874. $aJoins = $this->getJoins(false);
  875. $sqlQuery = "SELECT * FROM `{$this->aCurrent['table']}` " . (isset($aJoins['join']) ? ' ' . $aJoins['join'] : '' ) . $this->getRestriction() . (isset($aJoins['groupBy']) ? ' ' . $aJoins['groupBy'] : '') . ' ' . $this->getLimit(true);
  876. return count(BxDolDb::getInstance()->getAll($sqlQuery));
  877. }
  878. /**
  879. * Get total number of records
  880. * return total number of records
  881. */
  882. function getTotal ()
  883. {
  884. $aJoins = $this->getJoins(false);
  885. $sqlQuery = "SELECT COUNT(*) FROM `{$this->aCurrent['table']}` " . (isset($aJoins['join']) ? ' ' . $aJoins['join'] : '' ) . $this->getRestriction() . (isset($aJoins['groupBy']) ? ' ' . $aJoins['groupBy'] : '');
  886. return BxDolDb::getInstance()->getOne($sqlQuery);
  887. }
  888. /**
  889. * Check restriction params and make condition part of query
  890. * return $sqlWhere sql code of query for WHERE part
  891. */
  892. function getRestriction ()
  893. {
  894. $oDb = BxDolDb::getInstance();
  895. $sqlWhere = '';
  896. if (isset($this->aCurrent['restriction'])) {
  897. $aWhere[] = '1';
  898. foreach ($this->aCurrent['restriction'] as $sKey => $aValue) {
  899. $sqlCondition = '';
  900. if (isset($aValue['operator']) && isset($aValue['value']) && $aValue['value'] !== '' && $aValue['value'] !== false && $aValue['value'] !== null) {
  901. $sFieldTable = isset($aValue['table']) ? $aValue['table'] : $this->aCurrent['table'];
  902. $sqlCondition = "`{$sFieldTable}`.`{$aValue['field']}` ";
  903. switch ($aValue['operator']) {
  904. case 'empty value':
  905. $sqlCondition .= " = '' ";
  906. break;
  907. case 'not empty value':
  908. $sqlCondition .= " != '' ";
  909. break;
  910. case 'nothing':
  911. $sqlCondition = " 0 ";
  912. break;
  913. case 'against':
  914. $aCond = isset($aValue['field']) && strlen($aValue['field']) > 0 ? $aValue['field'] : $this->aCurrent['searchFields'];
  915. $sqlCondition = !empty($aCond) ? $this->getSearchFieldsCond($aCond, $aValue['value']) : "";
  916. break;
  917. case 'like':
  918. $sqlCondition .= "LIKE " . $oDb->escape('%' . $aValue['value'] . '%');
  919. break;
  920. case 'in':
  921. case 'not in':
  922. $sValuesString = $this->getMultiValues($aValue['value']);
  923. $sqlCondition .= strtoupper($aValue['operator']) . '(' . $sValuesString . ')';
  924. break;
  925. case 'in_set':
  926. $sqlCondition = "1 << (" . $sqlCondition . " - 1) & " . (int)$aValue['value'];
  927. break;
  928. default:
  929. $sqlCondition .= $aValue['operator'] . (isset($aValue['no_quote_value']) && $aValue['no_quote_value'] ? $aValue['value'] : $oDb->escape($aValue['value']));
  930. break;
  931. }
  932. }
  933. if (strlen($sqlCondition) > 0)
  934. $aWhere[] = $sqlCondition;
  935. }
  936. $sqlWhere .= "WHERE ". implode(' AND ', $aWhere) . (isset($this->aCurrent['restriction_sql']) ? $this->aCurrent['restriction_sql'] : '');
  937. }
  938. return $sqlWhere;
  939. }
  940. /**
  941. * Get limit part of query
  942. * return $sqlFrom code for limit part pf query
  943. */
  944. function getLimit ($isAddPlusOne = false)
  945. {
  946. if (!isset($this->aCurrent['paginate']))
  947. return;
  948. $sqlFrom = (int)$this->aCurrent['paginate']['start'] > 0 ? (int)$this->aCurrent['paginate']['start'] : 0;
  949. $sqlTo = $this->aCurrent['paginate']['perPage'] + ($isAddPlusOne ? 1 : 0);
  950. if ($sqlTo > 0)
  951. return 'LIMIT ' . $sqlFrom . ', ' . $sqlTo;
  952. }
  953. /**
  954. * Set sorting field of class
  955. */
  956. function setSorting ()
  957. {
  958. $this->aCurrent['sorting'] = isset($_GET[$this->aCurrent['name'] . '_mode']) ? $_GET[$this->aCurrent['name'] . '_mode'] : $this->aCurrent['sorting'];
  959. }
  960. /**
  961. * Get sorting part of query according current sorting mode
  962. * @param string $sSortType sorting type
  963. * return array with sql elements order and ownFields
  964. */
  965. function getSorting ($sSortType = 'last')
  966. {
  967. if (isset($this->aCurrent['sorting_sql']))
  968. return array('order' => $this->aCurrent['sorting_sql']);
  969. $aOverride = $this->getAlterOrder();
  970. if (is_array($aOverride) && !empty($aOverride))
  971. return $aOverride;
  972. $aSql = array();
  973. switch ($sSortType) {
  974. case 'rand':
  975. $aSql['order'] = "ORDER BY RAND()";
  976. break;
  977. case 'score':
  978. if (is_array($this->aCurrent['restriction']['keyword'])) {
  979. $sPseud = '';
  980. if ('on' == getParam('useLikeOperator')) {
  981. $aSql['order'] = "ORDER BY `score` DESC";
  982. $sPseud = 'score';
  983. }
  984. $aSql['ownFields'] .= $this->getSearchFieldsCond($this->aCurrent['searchFields'], $this->aCurrent['restriction']['keyword']['value'], $sPseud) . ',';
  985. }
  986. break;
  987. case 'none':
  988. $aSql['order'] = "";
  989. break;
  990. default:
  991. $aSql['order'] = "ORDER BY `date` DESC";
  992. }
  993. return $aSql;
  994. }
  995. /**
  996. * Return own varaint for sorting (redeclare if necessary)
  997. * return array of sql elements
  998. */
  999. function getAlterOrder ()
  1000. {
  1001. return array();
  1002. }
  1003. /**
  1004. * Set paginate fields of class according GET params 'start' and 'per_page'
  1005. * forcePage is need for setting most important number of current page
  1006. */
  1007. function setPaginate ()
  1008. {
  1009. $iStart = 0;
  1010. $iPerPage = 0;
  1011. //--- Check in aCurrent (low priority).
  1012. if(isset($this->aCurrent['paginate']['perPage']) && (int)$this->aCurrent['paginate']['perPage'] != 0)
  1013. $iPerPage = (int)$this->aCurrent['paginate']['perPage'];
  1014. //--- Check in aParams (middle priority).
  1015. if(isset($this->_aParams['per_page']) && (int)$this->_aParams['per_page'] != 0)
  1016. $iPerPage = (int)$this->_aParams['per_page'];
  1017. //--- Check in GET params (high priority).
  1018. if(isset($_GET['per_page']) && (int)$_GET['per_page'] != 0)
  1019. $iPerPage = (int)$_GET['per_page'];
  1020. //--- Trying to get from System settings.
  1021. if (empty($iPerPage))
  1022. $iPerPage = (int)getParam('sys_per_page_search_keyword_' . ($this->_bSingleSearch ? 'single' : 'plural'));
  1023. //--- Use default value in case of emergency.
  1024. if (empty($iPerPage))
  1025. $iPerPage = BX_DOL_SEARCH_RESULTS_PER_PAGE_DEFAULT;
  1026. if ($this->iPaginatePerPage != BX_DOL_SEARCH_RESULTS_PER_PAGE_DEFAULT)
  1027. $iPerPage = $this->iPaginatePerPage;
  1028. $this->aCurrent['paginate']['perPage'] = $iPerPage;
  1029. if(!isset($this->aCurrent['paginate']['forceStart'])) {
  1030. if(isset($this->_aParams['start']))
  1031. $iStart = (int)$this->_aParams['start'];
  1032. if(($iStartGet = bx_get('start')) !== false)
  1033. $iStart = (int)$iStartGet;
  1034. }
  1035. else
  1036. $iStart = (int)$this->aCurrent['paginate']['forceStart'];
  1037. if($iStart < 0)
  1038. $iStart = 0;
  1039. $this->aCurrent['paginate']['start'] = $iStart;
  1040. }
  1041. function unsetPaginate()
  1042. {
  1043. unset($this->aCurrent['paginate']);
  1044. }
  1045. /**
  1046. * Get sql where condition for search fields
  1047. * @param array of search fields
  1048. * @param string $sKeyword keyword value for search
  1049. * @param string sPseud for setting new name for generated set of fields in query
  1050. * return sql code of WHERE part in query
  1051. */
  1052. function getSearchFieldsCond ($aFields, $sKeyword, $sPseud = '')
  1053. {
  1054. if (!$sKeyword)
  1055. return '';
  1056. $sTable = empty($this->aCurrent['tableSearch']) ? $this->aCurrent['table'] : $this->aCurrent['tableSearch'];
  1057. $oDb = BxDolDb::getInstance();
  1058. $bLike = getParam('useLikeOperator');
  1059. if (!is_array($aFields))
  1060. $aFields = array($aFields);
  1061. if ($bLike == 'on') {
  1062. $sKeyword = $oDb->escape('%' . preg_replace('/\s+/', '%', $sKeyword) . '%');
  1063. $sSqlWhere = '';
  1064. foreach ($aFields as $sValue)
  1065. $sSqlWhere .= "`{$sTable}`.`$sValue` LIKE " . $sKeyword . " OR ";
  1066. $sSqlWhere = '(' . trim($sSqlWhere, 'OR ') . ')';
  1067. } else {
  1068. $sKeyword = $oDb->escape($sKeyword);
  1069. $sSqlWhere = '';
  1070. foreach ($aFields as $sValue)
  1071. $sSqlWhere .= "`{$sTable}`.`$sValue`, ";
  1072. $sSqlWhere = trim($sSqlWhere, ', ');
  1073. $sSqlWhere = " MATCH({$sSqlWhere}) AGAINST (" . $sKeyword . ") ";
  1074. if (!empty($sPseud))
  1075. $sSqlWhere .= " AS `$sPseud` ";
  1076. }
  1077. return $sSqlWhere;
  1078. }
  1079. /**
  1080. * Get set from several values for 'in' and 'not in' operators
  1081. * @param $aValues array of values
  1082. * return sql code for field with operator IN (NOT IN)
  1083. */
  1084. function getMultiValues ($aValues)
  1085. {
  1086. $oDb = BxDolDb::getInstance();
  1087. return $oDb->implode_escape($aValues);
  1088. }
  1089. /**
  1090. * System method for filling aPseud array.
  1091. * Fill field aPseud for current class (if you will use own getSearchUnit methods then not necessary to redeclare).
  1092. */
  1093. function _getPseud ()
  1094. {
  1095. }
  1096. /**
  1097. * Add replace markers. Markers are replaced in titles and browse urls
  1098. * @param $a array of markers as key => value
  1099. * @return true on success or false on error
  1100. */
  1101. public function addMarkers ($a)
  1102. {
  1103. if (empty($a) || !is_array($a))
  1104. return false;
  1105. $this->_aMarkers = array_merge ($this->_aMarkers, $a);
  1106. return true;
  1107. }
  1108. /**
  1109. * Replace provided markers in a string
  1110. * @param $mixed string or array to replace markers in
  1111. * @return string where all occured markers are replaced
  1112. */
  1113. protected function _replaceMarkers ($mixed)
  1114. {
  1115. return bx_replace_markers($mixed, $this->_aMarkers);
  1116. }
  1117. }
  1118. /** @} */