BxDolSearch.php 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762
  1. <?php
  2. /**
  3. * @package Dolphin Core
  4. * @copyright Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
  5. * @license CC-BY - http://creativecommons.org/licenses/by/3.0/
  6. */
  7. defined('BX_DOL') or die('hack attempt');
  8. /**
  9. * Search class for different content.
  10. *
  11. * Allows present content in main search area for all modules.
  12. *
  13. * To add taking part content from your module to search area you need add a record to `sys_objects_search` table:
  14. * \code
  15. * ID - autoincremented id for internal usage
  16. * ObjectName - your unique module name, with vendor prefix, lowercase and spaces are underscored
  17. * Title - language key for module's choicer in global search form
  18. * ClassName - class name which is responsible for search process ad showing result for found results
  19. * ClassPath - file where your ClassName is stored (if class is stored in template folder then you can use key {tmpl})
  20. * \endcode
  21. *
  22. * 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.
  23. *
  24. * Example of usage can be seen in the default Dolphin's modules (photos, sounds, events, sites, etc).
  25. *
  26. * Memberships/ACL:
  27. * Doesn't depend on user's membership.
  28. *
  29. * Alerts:
  30. * no alerts available
  31. *
  32. */
  33. class BxDolSearch extends BxDol {
  34. var $aClasses = array(); ///< array of all search classes
  35. var $aChoice = array(); ///< array of current search classes which were choosen in search area
  36. /**
  37. * Constructor
  38. * @param array $aChoice - array of choosen classes (will take a part only existing in `sys_objects_search` table)
  39. */
  40. function BxDolSearch ($aChoice = '') {
  41. parent::BxDol();
  42. $this->aClasses = BxDolDb::getInstance()->fromCache('sys_objects_search', 'getAllWithKey',
  43. 'SELECT `ID` as `id`,
  44. `Title` as `title`,
  45. `ClassName` as `class`,
  46. `ClassPath` as `file`,
  47. `ObjectName`
  48. FROM `sys_objects_search`', 'ObjectName'
  49. );
  50. if (is_array($aChoice) && !empty($aChoice)) {
  51. foreach ($aChoice as $sValue) {
  52. if (isset($this->aClasses[$sValue]))
  53. $this->aChoice[$sValue] = $this->aClasses[$sValue];
  54. }
  55. } else {
  56. $this->aChoice = $this->aClasses;
  57. }
  58. }
  59. /**
  60. * create units for all classes and calling their processing methods
  61. */
  62. function response () {
  63. $sCode = '';
  64. foreach ($this->aChoice as $sKey => $aValue) {
  65. if (!class_exists($aValue['class'])) {
  66. $sClassPath = str_replace('{tmpl}', BxDolTemplate::getInstance()->getCode(), $aValue['file']);
  67. require_once(BX_DIRECTORY_PATH_ROOT . $sClassPath);
  68. }
  69. $oEx = new $aValue['class']();
  70. $oEx->id = $aValue['id'];
  71. $sCode .= $oEx->processing();
  72. }
  73. return $sCode;
  74. }
  75. function getEmptyResult () {
  76. $sKey = _t('_Empty');
  77. return DesignBoxContent($sKey, MsgBox($sKey), 1);
  78. }
  79. }
  80. /*
  81. * Search class for processing search requests and displaying search results.
  82. *
  83. * Allows present content from modules on search params or internal (via fields of class) conditions.
  84. *
  85. * Example of usage (you can see example in any of default Dolhpin's modules):
  86. *
  87. * 1. Extends your own search class from this one (or from BxBaseSearchResult or BxBaseSearchResultSharedMedia classes)
  88. * 2. Set necessary fields of class (using as example BxFilesSearch):
  89. *
  90. * \code
  91. *
  92. * // main field of search class
  93. * $this->aCurrent = array(
  94. *
  95. * // name of module
  96. * 'name' => 'bx_files',
  97. *
  98. * // language key with name of module
  99. * 'title' => '_bx_files',
  100. *
  101. * // main content table
  102. * 'table' => 'bx_files_main',
  103. *
  104. * // array of all fields which can be choosen for select in result query
  105. * 'ownFields' => array('ID', 'Title', 'Uri', 'Desc', 'Date', 'Size', 'Ext', 'Views', 'Rate', 'RateCount', 'Type'),
  106. *
  107. * // array of fields which take a part in search by keyword (global search and related words only - in other cases leave it blank)
  108. * 'searchFields' => array('Title', 'Tags', 'Desc', 'Categories'),
  109. *
  110. * // array of join tables
  111. * // 'type' - type of join
  112. * // 'table' - join table
  113. * // 'mainField' - field from main table for 'on' condition
  114. * // 'onField' - field from joining table for 'on' condition
  115. * // 'joinFields' - array of fields from joining table
  116. *
  117. * 'join' => array(
  118. * 'profile' => array(
  119. * 'type' => 'left',
  120. * 'table' => 'Profiles',
  121. * 'mainField' => 'Owner',
  122. * 'onField' => 'ID',
  123. * 'joinFields' => array('NickName'),
  124. * ),
  125. * // ...
  126. * ),
  127. *
  128. * // array of search parameters
  129. * // 'value' - value of search parameter (can be number, string or array)
  130. * // '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
  131. * // 'operator' - operator between field and value (can be 'in', 'not in', '>', '<', 'against', 'like' and '=' by default),
  132. * // 'paramName' - GET param which will keep 'value' for pagianation
  133. *
  134. * 'restriction' => array(
  135. * 'activeStatus' => array('value'=>'approved', 'field'=>'Status', 'operator'=>'=', 'paramName' => 'status'),
  136. * 'albumType' => array('value'=>'', 'field'=>'Type', 'operator'=>'=', 'paramName'=>'albumType', 'table'=>'sys_albums'),
  137. * ),
  138. *
  139. * // array of pagination
  140. * // 'perPage' - units per page
  141. * // 'start' - show search results starting from this number
  142. * // 'num' - number of element to show on current page + 1
  143. *
  144. * 'paginate' => array('perPage' => 10, 'start' => 0, 'num' => 11),
  145. *
  146. * // sort mode - by default it was last (DESC by date)
  147. * 'sorting' => 'last',
  148. *
  149. * // mode of units presentation in search result, can be 'short' or 'full' view
  150. * 'view' => 'full',
  151. *
  152. * // field of id
  153. * 'ident' => 'ID',
  154. *
  155. * // rss feed array
  156. * 'rss' => array(
  157. * 'title' => '',
  158. * 'link' => '',
  159. * 'image' => '',
  160. * 'profile' => 0,
  161. * 'fields' => array (
  162. * 'Link' => '',
  163. * 'Title' => 'Title',
  164. * 'DateTimeUTS' => 'Date',
  165. * 'Desc' => 'Desc',
  166. * 'Photo' => '',
  167. * ),
  168. * ),
  169. * );
  170. *
  171. * // array of fields renamings
  172. * var $aPseud - filling in costructor by function _getPseud
  173. *
  174. * // unique identificator from `sys_objects_search` table
  175. * var $id;
  176. *
  177. * // indicator of rate connection
  178. * var $iRate
  179. *
  180. * // rate object field
  181. * var $oRate;
  182. *
  183. * // indicator of connection parts in display search data (for function addCustomParts)
  184. * var $bCustomParts;
  185. *
  186. * \endcode
  187. *
  188. * Memberships/ACL:
  189. * Doesn't depend on user's membership.
  190. *
  191. * Alerts:
  192. * no alerts available
  193. *
  194. */
  195. class BxDolSearchResult {
  196. var $aCurrent;
  197. var $aPseud;
  198. var $id;
  199. var $iRate = 1;
  200. var $oRate = null;
  201. var $bCustomParts = false;
  202. var $bDisplayEmptyMsg = false;
  203. var $sDisplayEmptyMsgKey = '';
  204. var $bProcessPrivateContent = true;
  205. protected $_aMarkers = array ();
  206. /**
  207. * constructor
  208. * filling identificator field
  209. */
  210. function BxDolSearchResult () {
  211. if (isset($this->aPseud['id']))
  212. $this->aCurrent['ident'] = $this->aPseud['id'];
  213. }
  214. /**
  215. * Display empty message if there is no content, custom empty message can be used.
  216. * @param $b - boolan value to enable or disable 'empty' message
  217. * @param $sLangKey [optional] - custom 'empty' message
  218. */
  219. public function setDisplayEmptyMsg($b, $sLangKey = '') {
  220. $this->bDisplayEmptyMsg = $b;
  221. if ($sLangKey)
  222. $this->sDisplayEmptyMsgKey = $sLangKey;
  223. }
  224. /**
  225. * Perform privacy checking for every unit
  226. * @param $b - boolan value to enable or disable privacy checking
  227. */
  228. public function setProcessPrivateContent ($b) {
  229. $this->bProcessPrivateContent = $b;
  230. }
  231. /**
  232. * Get html box of search results (usually used in grlobal search)
  233. * @return html code
  234. */
  235. function processing () {
  236. $sCode = $this->displayResultBlock();
  237. if ($this->aCurrent['paginate']['num'] > 0) {
  238. $sPaginate = $this->showPagination();
  239. $sCode = $this->displaySearchBox($sCode, $sPaginate);
  240. }
  241. else {
  242. $sCode = $this->bDisplayEmptyMsg ? $this->displaySearchBox(MsgBox(_t($this->sDisplayEmptyMsgKey ? $this->sDisplayEmptyMsgKey : '_Empty'))) : '';
  243. }
  244. return $sCode;
  245. }
  246. /**
  247. * Get html output of search result
  248. * @return html code
  249. */
  250. function displayResultBlock () {
  251. $aData = $this->getSearchData();
  252. if (count($aData) > 0) {
  253. $sCode .= $this->addCustomParts();
  254. foreach ($aData as $iKey => $aValue) {
  255. $sCode .= $this->displaySearchUnit($aValue);
  256. }
  257. }
  258. return $sCode;
  259. }
  260. /**
  261. * Add different code before html output (usually redeclared)
  262. * no return result
  263. */
  264. function addCustomParts () {
  265. }
  266. /**
  267. * Get array for rss output
  268. */
  269. function rss () {
  270. if (!isset($this->aCurrent['rss']['fields']) || !isset($this->aCurrent['rss']['link']))
  271. return '';
  272. $aData = $this->getSearchData();
  273. $f = &$this->aCurrent['rss']['fields'];
  274. if ($aData) {
  275. foreach ($aData as $k => $a) {
  276. $aData[$k][$f['Link']] = $this->getRssUnitLink ($a);
  277. }
  278. }
  279. bx_import('BxDolRssFactory');
  280. $oRss = new BxDolRssFactory ();
  281. return $oRss->GenRssByCustomData(
  282. $aData,
  283. isset($this->aCurrent['rss']['title']) && $this->aCurrent['rss']['title'] ? $this->aCurrent['rss']['title'] : $this->aCurrent['title'],
  284. $this->aCurrent['rss']['link'],
  285. $this->aCurrent['rss']['fields'],
  286. isset($this->aCurrent['rss']['image']) ? $this->aCurrent['rss']['image'] : '',
  287. isset($this->aCurrent['rss']['profile']) ? $this->aCurrent['rss']['profile'] : 0
  288. );
  289. }
  290. /**
  291. * Return rss unit link (redeclared)
  292. */
  293. function getRssUnitLink (&$a) {
  294. // override this functions to return permalink to rss unit
  295. }
  296. /**
  297. * Naming fields in query's body
  298. * @param string $sFieldName name of field
  299. * @param string $sTableName name of field's table
  300. * $param string $sOperator of field's calculation (like MAX)
  301. * @param boolean $bRenameMode indicator for renaming and unsetting fields from field of class $this->aPseud
  302. * return $sqlUnit sql code and unsetting elements from aPseud field
  303. */
  304. function setFieldUnit ($sFieldName, $sTableName, $sOperator = '', $bRenameMode = true) {
  305. if (strlen($sOperator) > 0) {
  306. $sqlUnit = "$sOperator(`$sTableName`.`$sFieldName`)";
  307. }
  308. else
  309. $sqlUnit = "`$sTableName`.`$sFieldName`";
  310. if (isset($this->aPseud) && $bRenameMode !== false) {
  311. $sKey = array_search($sFieldName, $this->aPseud);
  312. if ($sKey !== false) {
  313. $sqlUnit .= " as `$sKey`";
  314. unset($this->aPseud[$sKey]);
  315. }
  316. }
  317. return $sqlUnit . ', ';
  318. }
  319. /**
  320. * Get html code of of every search unit
  321. * @param array $aData array of every search unit
  322. * return html code
  323. */
  324. function displaySearchUnit ($aData) {
  325. }
  326. /**
  327. * Get html code of search box with search results
  328. * @param string $sCode html code of search results
  329. * $param $sPaginate html code of paginate
  330. * return html code
  331. */
  332. function displaySearchBox ($sCode, $sPaginate = '') {
  333. }
  334. /**
  335. * Get html code of pagination
  336. */
  337. function showPagination ($bAdmin = false, $bChangePage = true, $bPageReload = true) {
  338. }
  339. /**
  340. * Get array of data with search results
  341. * return array with data
  342. */
  343. function getSearchData () {
  344. $this->aPseud = $this->_getPseud();
  345. $this->setConditionParams();
  346. if ($this->aCurrent['paginate']['num'] > 0) {
  347. $aData = $this->getSearchDataByParams();
  348. return $aData;
  349. }
  350. }
  351. /**
  352. * Get array with code for sql elements
  353. * @param boolean #bREnameMode indicator of renmaing fields
  354. * return array with joinFields, ownFields, groupBy and join elements
  355. */
  356. function getJoins ($bRenameMode = true) {
  357. $aSql = array();
  358. // joinFields & join
  359. if (isset($this->aCurrent['join']) && is_array($this->aCurrent['join'])) {
  360. $aSql = array('join'=>'', 'ownFields'=>'', 'joinFields'=>'', 'groupBy'=>'');
  361. foreach ($this->aCurrent['join'] as $sKey => $aValue) {
  362. if (is_array($aValue['joinFields'])) {
  363. foreach ($aValue['joinFields'] as $sValue) {
  364. $aSql['joinFields'] .= $this->setFieldUnit($sValue, $aValue['table'], isset($aValue['operator']) ? $aValue['operator'] : null, $bRenameMode);
  365. }
  366. }
  367. // group by
  368. if (isset($aValue['groupTable']))
  369. $aSql['groupBy'] = "GROUP BY `{$aValue['groupTable']}`.`{$aValue['groupField']}`, ";
  370. $sOn = isset($aValue['mainTable']) ? $aValue['mainTable'] : $this->aCurrent['table'];
  371. $aSql['join'] .= " {$aValue['type']} JOIN `{$aValue['table']}` ON `{$aValue['table']}`.`{$aValue['onField']}`=`$sOn`.`{$aValue['mainField']}`";
  372. $aSql['ownFields'] .= $this->setFieldUnit($aValue['mainField'], $sOn, '', $bRenameMode);
  373. }
  374. $aSql['joinFields'] = trim($aSql['joinFields'], ', ');
  375. $aSql['groupBy'] = trim($aSql['groupBy'], ', ');
  376. }
  377. return $aSql;
  378. }
  379. /**
  380. * Concat sql parts of query, run it and return result array
  381. * @param $aParams addon param
  382. * return $aData multivariate array
  383. */
  384. function getSearchDataByParams ($aParams = '') {
  385. $aSql = array('ownFields'=>'', 'joinFields'=>'', 'order'=>'');
  386. // searchFields
  387. foreach ($this->aCurrent['ownFields'] as $sValue) {
  388. $aSql['ownFields'] .= $this->setFieldUnit($sValue, $this->aCurrent['table']);
  389. }
  390. // joinFields & join
  391. $aJoins = $this->getJoins();
  392. if (!empty($aJoins)) {
  393. $aSql['ownFields'] .= $aJoins['ownFields'];
  394. $aSql['joinFields'] .= $aJoins['joinFields'];
  395. $aSql['join'] = $aJoins['join'];
  396. $aSql['groupBy'] = $aJoins['groupBy'];
  397. }
  398. else
  399. $aSql['ownFields'] = trim($aSql['ownFields'], ', ');
  400. // from
  401. $aSql['from'] = " FROM `{$this->aCurrent['table']}`";
  402. // where
  403. $aSql['where'] = $this->getRestriction();
  404. // limit
  405. $aSql['limit'] = $this->getLimit();
  406. // sorting
  407. $this->setSorting();
  408. $aSort = $this->getSorting($this->aCurrent['sorting']);
  409. foreach ($aSort as $sKey => $sValue)
  410. $aSql[$sKey] .= $sValue;
  411. // rate part
  412. $aRate = $this->getRatePart();
  413. if (is_array($aRate)) {
  414. foreach ($aRate as $sKey => $sValue)
  415. $aSql[$sKey] .= $sValue;
  416. }
  417. // execution
  418. $sqlQuery = "SELECT " . $aSql['ownFields'];
  419. if (isset($aSql['joinFields']))
  420. $sqlQuery .= ' ' . $aSql['joinFields'];
  421. $sqlQuery .= ' ' . $aSql['from'];
  422. if (isset($aSql['join']))
  423. $sqlQuery .= ' ' . $aSql['join'];
  424. if (isset($aSql['where']))
  425. $sqlQuery .= ' ' . $aSql['where'];
  426. if (isset($aSql['groupBy']))
  427. $sqlQuery .= ' ' . $aSql['groupBy'];
  428. if (isset($aSql['order']))
  429. $sqlQuery .= ' ' . $aSql['order'];
  430. if (isset($aSql['limit']))
  431. $sqlQuery .= ' ' . $aSql['limit'];
  432. // echoDbg($sqlQuery);
  433. $aRes = BxDolDb::getInstance()->getAll($sqlQuery);
  434. return $aRes;
  435. }
  436. /**
  437. * Set class fields condition params and paginate array
  438. */
  439. function setConditionParams() {
  440. $aWhere = array();
  441. $aWhere[] = '1';
  442. $sKeyword = bx_process_input(bx_get('keyword'));
  443. if ($sKeyword !== false)
  444. $this->aCurrent['restriction']['keyword'] = array(
  445. 'value' => $sKeyword,
  446. 'field' => '',
  447. 'operator' => 'against'
  448. );
  449. if (isset($_GET['ownerName'])) {
  450. $sName = bx_process_input($_GET['ownerName']);
  451. $iUser = (int)BxDolProfileQuery::getInstance()->getIdByNickname($sName);
  452. BxDolMenu::getInstance()->setCurrentProfileID($iUser);
  453. }
  454. elseif (isset($_GET['userID']))
  455. $iUser = bx_process_input($_GET['userID'], BX_DATA_INT);
  456. if (!empty($iUser))
  457. $this->aCurrent['restriction']['owner']['value'] = $iUser;
  458. $this->setPaginate();
  459. $iNum = $this->getNum();
  460. if ($iNum > 0) {
  461. $this->aCurrent['paginate']['num'] = $iNum;
  462. }
  463. else {
  464. $this->aCurrent['paginate']['num'] = 0;
  465. }
  466. }
  467. /**
  468. * Check number of records on current page
  469. * return number of records on current page + 1
  470. */
  471. function getNum () {
  472. $aJoins = $this->getJoins(false);
  473. $sqlQuery = "SELECT * FROM `{$this->aCurrent['table']}` " . (isset($aJoins['join']) ? $aJoins['join'] : '' ). $this->getRestriction() . (isset($aJoins['groupBy']) ? $aJoins['groupBy'] : '') . ' ' . $this->getLimit(true);
  474. return count(BxDolDb::getInstance()->getAll($sqlQuery));
  475. }
  476. /**
  477. * Check restriction params and make condition part of query
  478. * return $sqlWhere sql code of query for WHERE part
  479. */
  480. function getRestriction () {
  481. $oDb = BxDolDb::getInstance();
  482. $sqlWhere = '';
  483. if (isset($this->aCurrent['restriction'])) {
  484. $aWhere[] = '1';
  485. foreach ($this->aCurrent['restriction'] as $sKey => $aValue) {
  486. $sqlCondition = '';
  487. if (isset($aValue['operator']) && isset($aValue['value']) && $aValue['value'] !== '' && $aValue['value'] !== false && $aValue['value'] !== null) {
  488. $sFieldTable = isset($aValue['table']) ? $aValue['table'] : $this->aCurrent['table'];
  489. $sqlCondition = "`{$sFieldTable}`.`{$aValue['field']}` ";
  490. switch ($aValue['operator']) {
  491. case 'against':
  492. $aCond = isset($aValue['field']) && strlen($aValue['field']) > 0 ? $aValue['field'] : $this->aCurrent['searchFields'];
  493. $sqlCondition = !empty($aCond) ? $this->getSearchFieldsCond($aCond, $aValue['value']) : "";
  494. break;
  495. case 'like':
  496. $sqlCondition .= "LIKE '%" . $oDb->escape($aValue['value']) . "%'";
  497. break;
  498. case 'in':
  499. case 'not in':
  500. $sValuesString = $this->getMultiValues($aValue['value']);
  501. $sqlCondition .= strtoupper($aValue['operator']) . '('.$sValuesString.')';
  502. break;
  503. default:
  504. $sqlCondition .= $aValue['operator'] . (isset($aValue['no_quote_value']) && $aValue['no_quote_value'] ? $aValue['value'] : "'" . $oDb->escape($aValue['value']) . "'");
  505. break;
  506. }
  507. }
  508. if (strlen($sqlCondition) > 0)
  509. $aWhere[] = $sqlCondition;
  510. }
  511. $sqlWhere .= "WHERE ". implode(' AND ', $aWhere);
  512. }
  513. return $sqlWhere;
  514. }
  515. /**
  516. * Get limit part of query
  517. * return $sqlFrom code for limit part pf query
  518. */
  519. function getLimit ($isAddPlusOne = false) {
  520. if (!isset($this->aCurrent['paginate']))
  521. return;
  522. $sqlFrom = (int)$this->aCurrent['paginate']['start'] > 0 ? (int)$this->aCurrent['paginate']['start'] : 0;
  523. $sqlTo = $this->aCurrent['paginate']['perPage'] + ($isAddPlusOne ? 1 : 0);
  524. if ($sqlTo > 0)
  525. return 'LIMIT ' . $sqlFrom . ', ' . $sqlTo;
  526. }
  527. /**
  528. * Set sorting field of class
  529. */
  530. function setSorting () {
  531. $this->aCurrent['sorting'] = isset($_GET[$this->aCurrent['name'] . '_mode']) ? $_GET[$this->aCurrent['name'] . '_mode'] : $this->aCurrent['sorting'];
  532. }
  533. /**
  534. * Get sorting part of query according current sorting mode
  535. * @param string $sSortType sorting type
  536. * return array with sql elements order and ownFields
  537. */
  538. function getSorting ($sSortType = 'last') {
  539. $aOverride = $this->getAlterOrder();
  540. if (is_array($aOverride) && !empty($aOverride))
  541. return $aOverride;
  542. $aSql = array();
  543. switch ($sSortType) {
  544. case 'rand':
  545. $aSql['order'] = "ORDER BY RAND()";
  546. break;
  547. case 'top':
  548. $sHow = "DESC";
  549. $aSql['order'] = "ORDER BY `Rate` $sHow, `RateCount` $sHow, `date` $sHow";
  550. break;
  551. case 'score':
  552. if (is_array($this->aCurrent['restriction']['keyword'])) {
  553. $aSql['order'] = "ORDER BY `score` DESC";
  554. $aSql['ownFields'] .= $this->getSearchFieldsCond($this->aCurrent['searchFields'], $this->aCurrent['restriction']['keyword']['value'], 'score'). ', ';
  555. }
  556. break;
  557. case 'voteTime':
  558. $aSql['order'] = "ORDER BY `voteTime` DESC";
  559. break;
  560. case 'none':
  561. $aSql['order'] = "";
  562. break;
  563. default:
  564. $aSql['order'] = "ORDER BY `date` DESC";
  565. }
  566. return $aSql;
  567. }
  568. /**
  569. * Return own varaint for sorting (redeclare if necessary)
  570. * return array of sql elements
  571. */
  572. function getAlterOrder () {
  573. return array();
  574. }
  575. /**
  576. * Set paginate fields of class according GET params 'start' and 'per_page'
  577. * forcePage is need for setting most important number of current page
  578. */
  579. function setPaginate () {
  580. $this->aCurrent['paginate']['perPage'] = (isset($_GET['per_page']) && (int)$_GET['per_page'] != 0) ? (int)$_GET['per_page'] : $this->aCurrent['paginate']['perPage'];
  581. if (empty($this->aCurrent['paginate']['perPage']))
  582. $this->aCurrent['paginate']['perPage'] = 10;
  583. $this->aCurrent['paginate']['start'] = isset($this->aCurrent['paginate']['forceStart'])
  584. ? (int)$this->aCurrent['paginate']['forceStart']
  585. : (empty($_GET['start']) ? 0 : (int)$_GET['start']);
  586. if ($this->aCurrent['paginate']['start'] < 0)
  587. $this->aCurrent['paginate']['start'] = 0;
  588. }
  589. /**
  590. * Get sql where condition for search fields
  591. * @param array of search fields
  592. * @param string $sKeyword keyword value for search
  593. * @param string sPseud for setting new name for generated set of fields in query
  594. * return sql code of WHERE part in query
  595. */
  596. function getSearchFieldsCond ($aFields, $sKeyword, $sPseud = '') {
  597. if (!$sKeyword)
  598. return;
  599. $oDb = BxDolDb::getInstance();
  600. $bLike = getParam('useLikeOperator');
  601. $sKeyword = $oDb->escape($sKeyword);
  602. $aSql = array(
  603. 'function' => 'MATCH',
  604. 'operator' => 'AGAINST',
  605. 'word' => $sKeyword
  606. );
  607. if ($bLike == 'on') {
  608. $aSql = array(
  609. 'function' => 'CONCAT', // TODO: get rid of CONCAT !!!
  610. 'operator' => 'LIKE',
  611. 'word' => '%' . preg_replace('/\s+/', '%', $sKeyword) . '%'
  612. );
  613. }
  614. $sqlWhere = " {$aSql['function']}(";
  615. if (is_array($aFields)) {
  616. foreach ($aFields as $sValue)
  617. $sqlWhere .= "`{$this->aCurrent['table']}`.`$sValue`, ";
  618. } else {
  619. $sqlWhere .= "`{$this->aCurrent['table']}`.`$aFields`";
  620. }
  621. $sqlWhere = trim($sqlWhere, ', ') . ") {$aSql['operator']} ('{$aSql['word']}')";
  622. if (strlen($sPseud) > 0)
  623. $sqlWhere .= " as `$sPseud`";
  624. return $sqlWhere;
  625. }
  626. /**
  627. * Get set from several values for 'in' and 'not in' operators
  628. * @param $aValues array of values
  629. * return sql code for field with operator IN (NOT IN)
  630. */
  631. function getMultiValues ($aValues) {
  632. $oDb = BxDolDb::getInstance();
  633. return $oDb->implode_escape($aValues);
  634. }
  635. /**
  636. * Generate rate object if it wasn't created yet
  637. */
  638. function getRatePart () {
  639. if ($this->iRate == 1) {
  640. bx_import('BxTemplVote');
  641. $this->oRate = new BxTemplVote($this->aCurrent['name'], 0, 0);
  642. }
  643. }
  644. /**
  645. * System method for filling aPseud array.
  646. * Fill field aPseud for current class (if you will use own getSearchUnit methods then not necessary to redeclare).
  647. */
  648. function _getPseud () {
  649. }
  650. /**
  651. * Add replace markers. Markers are replaced in titles and browse urls
  652. * @param $a array of markers as key => value
  653. * @return true on success or false on error
  654. */
  655. public function addMarkers ($a) {
  656. if (empty($a) || !is_array($a))
  657. return false;
  658. $this->_aMarkers = array_merge ($this->_aMarkers, $a);
  659. return true;
  660. }
  661. /**
  662. * Replace provided markers in a string
  663. * @param $mixed string or array to replace markers in
  664. * @return string where all occured markers are replaced
  665. */
  666. protected function _replaceMarkers ($mixed) {
  667. if (empty($this->_aMarkers))
  668. return $mixed;
  669. if (is_array($mixed)) {
  670. foreach ($mixed as $sKey => $sValue)
  671. $mixed[$sKey] = $this->_replaceMarkers ($sValue);
  672. } else {
  673. foreach ($this->_aMarkers as $sKey => $sValue)
  674. $mixed = str_replace('{' . $sKey . '}', $sValue, $mixed);
  675. }
  676. return $mixed;
  677. }
  678. }