BxDolGrid.php 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  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. /**
  10. * Grid for any content.
  11. *
  12. * It allows to display some data as grid with ready to use features:
  13. * - paginate
  14. * - reordering
  15. * - sorting
  16. * - search
  17. * - actions
  18. *
  19. * The advantages of the this system:
  20. * - Less code to write - so you can concentrate on the main functionality.
  21. * - Flexibility - you can turn on/off ready features or override it for the custom behavior.
  22. *
  23. * Grid is working together with Paginate to look through the data in the grid. @see BxDolPaginate
  24. *
  25. * @section grid_create Creating the new Grid object:
  26. *
  27. * 1. add record to 'sys_objects_grid' table:
  28. *
  29. * - object: name of the grid object, in the format: vendor prefix, underscore, module prefix, underscore, internal identifier or nothing; for example: bx_profiles_admin - to display profiles in admin panel.
  30. * - source_type: type of the source data:
  31. * - Sql: the source is SQL query.
  32. * - Array: the source is serialized array.
  33. * - source: the source data, different for each 'source_type':
  34. * - Sql: the SQL query string, without ORDER BY and LIMIT clauses, these clauses are added automatically for sorting, pagination and filtering.
  35. * - Array: 2 dimentional serialized array string.
  36. * - table: table name (if 'source_type' is 'Sql'), to automatically update order field and delete records.
  37. * - field_id: name of the ID field.
  38. * - field_order: name of the order field.
  39. * - field_active: name of the field which determines if the row is active or disabled. This field is used to display row as disabled (ususally as grayed out).
  40. * The following functions can be overrided for custom behavior:
  41. * - '_switcherChecked2State' and '_switcherState2Checked': override these functions if value of 'field_active' field is different from '0' and '1'
  42. * - '_enable': for custom behavior upon activation/deactivation
  43. * - '_isRowDisabled' and '_isCheckboxDisabled': for displaying disabled rows which is not related to 'field_active' field.
  44. * - '_isSwitcherOn': to display rows which are active/disabled by default.
  45. * - paginate_url: URL of the page for the grid, set it empty for AJAX paginate, or specify the url for regular page reloading in paginate, with the following markers:
  46. * - {start}: starting record to show data from.
  47. * - {per_page}: number of records per one page.
  48. * - paginate_per_page: number of records per one page.
  49. * - paginate_simple: show full or simple paginate, the following values are supported:
  50. * - NULL: show full(big) paginate.
  51. * - ''(empty string): show simple(small) paginate.
  52. * - 'some url': show simple(small) paginate with "View All" link to the specified URL.
  53. * - paginate_get_start: GET variable name for 'start'.
  54. * - paginate_get_per_page: GET variable name for 'per_page'.
  55. * - filter_fields: comma separated list of field names to search in; if field contains language key then it is better to specify it in 'filter_fields_translatable'.
  56. * - filter_fields_translatable: comma separated list of field names to search in its translations; enter field name here if field contains language key.
  57. * - filter_mode: search mode:
  58. * - like: use SQL LIKE expression for search, if 'source_type' is not 'Sql' it doesn't matter.
  59. * - fulltext: use MATCH ... AGAINST expression for search, if 'source_type' is not 'Sql' it doesn't matter.
  60. * - auto: use 'like' or 'fulltext', depending on 'useLikeOperator' setting option.
  61. * - sorting_fields: comma separated field names, which will be allowed for sorting, if field contains language keys, specify it in 'sorting_fields_translatable' field instead.
  62. * - sorting_fields_translatable: comma separated field names which need to be sorted by translations, enter field name here if field contains language key.
  63. * - visible_for_levels: bit field with set of member level ids. To use member level id in bit field - the level id minus 1 as power of 2 is used, for example:
  64. * - user level id = 1 -> 2^(1-1) = 1
  65. * - user level id = 2 -> 2^(2-1) = 2
  66. * - user level id = 3 -> 2^(3-1) = 4
  67. * - user level id = 4 -> 2^(4-1) = 8
  68. * - override_class_name: user defined class name which is derived from BxTemplGrid.
  69. * - override_class_file: the location of the user defined class, leave it empty if class is located in system folders.
  70. *
  71. * 2. Specify field names (columns in the grid) in sys_grid_fields table:
  72. *
  73. * - object: name of the Grid object.
  74. * - name: name of the field, it must refer to the SQL field name in the case of 'Sql' 'source_type' or index of the 2 dimentional array in the case of 'Array' 'source_type'.
  75. * - title: title of the field, the language key.
  76. * - width: width of the column in % or px, pt, etc.
  77. * - translatable: if field contains language key and it is needed to display translation for this key - set it to 1, by default 0.
  78. * - params: searialized array of additional params:
  79. * - display: display function from BxDolFormCheckerHelper class, for example to convert unix timestamp to the regular date/time string.
  80. * - attr_cell: tag attributes for the data cell.
  81. * - attr_head: tag attributes for the header cell.
  82. * - order: order of the field.
  83. *
  84. * There are some fields which are always available, additionally to the provided set of fields:
  85. *
  86. * - order: display column as dragable handle, it makes sense if you have data ordered by some field
  87. * and it is specified in field_order, field_id and table fields; reordering is not correctly
  88. * working with paginate, so make sure that paginate_per_page number is big enough to show all records;
  89. * reordering is working with Sql source_type.
  90. * - checkbox: display column with checkboxes, so several records can be selected for bulk action;
  91. * you need to specify 'field_id' field, so every checkbox have unique row id;
  92. * you need to specify bulk actions separately in 'sys_grid_actions' table;
  93. * you can override '_isCheckboxSelected' function to display checkbox as checked by default.
  94. * - actions: display column with single actions, displayed as buttons; you need to specify field_id field,
  95. * so every action is provided with unique row id; you need to specify single actions separately in sys_grid_actions table.
  96. *
  97. * 3. Add actions to sys_grid_actions table:
  98. *
  99. * - object: name of the Grid object.
  100. * - type: action type, one of the following:
  101. * - bulk: bulk action, to perform on the set of records, the action is usually displaed below the grid.
  102. * - single: simple action, to perform on one record, the action is usually displayed in the grid row.
  103. * - independent: independent actionm which is not related to any rowm the action is usually displayed above the grid.
  104. * - name: action name.
  105. * - title: title of the action, the language key.
  106. * - icon: display action as icon, title need to be empty in this case.
  107. * - confirm: ask confirmation before performing the action, 0 or 1.
  108. * - order: order of the action in particular actions set by type.
  109. *
  110. * Usually you need to handle actions manually, but there are several actions which are available by default:
  111. *
  112. * - delete: delete the record, it works automatically when 'source_type' is 'Sql' and 'field_id', 'table' fields are specified.
  113. *
  114. *
  115. *
  116. * @section grid_display_custom_cell Displaying custom cell
  117. *
  118. * Cell is displayed with default design. It is possible to easily customize its design by specifying custom attributes as 'attr_cell' in params field in sys_grid_fields table.
  119. *
  120. * If it is not enough, you can customize it even more by adding the method to your custom class with the following format:
  121. * _getCell[field name]
  122. * where [field name] is the name of the field you want to have custom look with the capital first letter.
  123. *
  124. * For example:
  125. *
  126. * @code
  127. * protected function _getCellStatus ($mixedValue, $sKey, $aField, $aRow) {
  128. *
  129. * $sAttr = $this->_convertAttrs(
  130. * $aField, 'attr_cell',
  131. * false,
  132. * isset($aField['width']) ? 'width:' . $aField['width'] : false // add default styles
  133. * );
  134. * return '<td ' . $sAttr . '><span style="background-color:' . ('Active' == $mixedValue ? '#cfc' : '#fcc') . '">' . $mixedValue . '</span></td>';
  135. * }
  136. * @endcode
  137. *
  138. * Above example is displaying user's status using different colors depending on the status value. Please note that you need to convert attributes by adding some default classes or styles if you need.
  139. *
  140. *
  141. *
  142. * @section grid_display_custom_header Displaying custom column header
  143. *
  144. * This is working similar to displaying custom cell. It easily customize its design by specifying custom attributes as 'attr_head' in params field in sys_grid_fields table.
  145. * If it is not enough, you can customize it even more by adding the method to your custom class with the following format:
  146. * _getCellHeader[field name]
  147. * where [field name] is the name of the field you want to have custom look with the capital first letter.
  148. *
  149. * For example:
  150. *
  151. * @code
  152. * protected function _getCellHeaderStatus ($sKey, $aField) {
  153. * $s = parent::_getCellHeaderDefault($sKey, $aField);
  154. * return preg_replace ('/<th(.*?)>(.*?)<\/th>/', '<th$1><img src="' . BxDolTemplate::getInstance()->getIconUrl('user.png') . '"></th>', $s);
  155. * }
  156. * @endcode
  157. *
  158. * The above example replaces column header text with the image.
  159. *
  160. *
  161. *
  162. * @section grid_display_custom_action Displaying custom action
  163. *
  164. * All actions are displayed as buttons. Bulk and independent actions are displaed as big buttons and single actions are displayed as small buttons.
  165. *
  166. * It is possible to completely customize it by adding the following method to your custom class:
  167. * _getAction[action name]
  168. * where [action name] is the action name with the capital first letter.
  169. *
  170. * For example:
  171. *
  172. * @code
  173. * protected function _getActionCustom1 ($sType, $sKey, $a, $isSmall = false) {
  174. * $sAttr = $this->_convertAttrs(
  175. * $a, 'attr',
  176. * 'bx-btn bx-def-margin-sec-left' . ($isSmall ? ' bx-btn-small' : '') // add default classes
  177. * );
  178. * return '<button ' . $sAttr . ' onclick="$(this).off(); alert(\'default behaviour is overrided, so the action is not performed\');">' . $a['title'] . '</button>';
  179. * }
  180. * @endcode
  181. *
  182. * The above example disables default onclick event and just displays an alert. Please note that you need to convert attributes by adding some default classes or styles if you need.
  183. *
  184. *
  185. *
  186. * @section grid_add_action_handler Add action handler
  187. *
  188. * As it was mentioned earlier only several actions can be handled automatically, all other actions must be processed manually.
  189. * To add action handler you need to add method to your custom class with the following format:
  190. * performAction[action name]
  191. * where [action name] is the action name with the capital first letter.
  192. *
  193. * For example:
  194. *
  195. * @code
  196. * public function performActionApprove() {
  197. *
  198. * $iAffected = 0;
  199. * $aIds = bx_get('ids');
  200. * if (!$aIds || !is_array($aIds)) {
  201. * echoJson(array());
  202. * exit;
  203. * }
  204. *
  205. * $aIdsAffected = array ();
  206. * foreach ($aIds as $mixedId) {
  207. * if (!$this->_approve($mixedId))
  208. * continue;
  209. * $aIdsAffected[] = $mixedId;
  210. * $iAffected++;
  211. * }
  212. *
  213. * echoJson(array(
  214. * 'msg' => $iAffected > 0 ? sprintf("%d profiles successfully activated", $iAffected) : "Profile(s) activation failed",
  215. * 'grid' => $this->getCode(false),
  216. * 'blink' => $aIdsAffected,
  217. * ));
  218. * }
  219. *
  220. * protected function _approve ($mixedId) {
  221. * $oDb = BxDolDb::getInstance();
  222. * $sTable = $this->_aOptions['table'];
  223. * $sFieldId = $this->_aOptions['field_id'];
  224. * $sQuery = $oDb->prepare("UPDATE `{$sTable}` SET `Status` = 'Active' WHERE `{$sFieldId}` = ?", $mixedId);
  225. * return $oDb->query($sQuery);
  226. * }
  227. * @endcode
  228. *
  229. * The action can be used as 'single' or 'bulk', in the case of 'single' action 'ids' array always has one element.
  230. *
  231. * As the result, action must outputs JSON array, which is done by echoJson function.
  232. * The defined indexes in the array determines behavior after action is performed, the following behaviors are supported:
  233. *
  234. * - msg: display javascript alert message.
  235. * - grid: reload grid data with the provided HTML code.
  236. * - popup: display popup with the provided HTML code.
  237. * - blink: highlight(blink effect) the specified rows, by the ids.
  238. *
  239. */
  240. class BxDolGrid extends BxDolFactory implements iBxDolFactoryObject, iBxDolReplaceable
  241. {
  242. protected $_aMarkers = array ();
  243. protected $_sObject;
  244. protected $_aOptions;
  245. protected $_aBrowseParams;
  246. protected $_sDefaultSortingOrder = 'ASC';
  247. protected $_iTotalCount = 0;
  248. /**
  249. * Constructor
  250. * @param $aOptions array of grid options
  251. */
  252. protected function __construct($aOptions)
  253. {
  254. parent::__construct();
  255. $this->_sObject = $aOptions['object'];
  256. $this->_aOptions = $aOptions;
  257. $sBrowseParams = bx_get('bp');
  258. if(!empty($sBrowseParams)) {
  259. $aBrowseParams = bx_process_input(unserialize(urldecode($sBrowseParams)));
  260. if(!empty($aBrowseParams) && is_array($aBrowseParams))
  261. $this->setBrowseParams($aBrowseParams);
  262. }
  263. }
  264. /**
  265. * Get grid object instance by object name
  266. * @param $sObject object name
  267. * @return object instance or false on error
  268. */
  269. public static function getObjectInstance($sObject, $oTemplate = false)
  270. {
  271. if (isset($GLOBALS['bxDolClasses']['BxDolGrid!'.$sObject]))
  272. return $GLOBALS['bxDolClasses']['BxDolGrid!'.$sObject];
  273. $aObject = BxDolGridQuery::getGridObject($sObject);
  274. if (!$aObject || !is_array($aObject))
  275. return false;
  276. $sClass = 'BxTemplGrid';
  277. if (!empty($aObject['override_class_name'])) {
  278. $sClass = $aObject['override_class_name'];
  279. if (!empty($aObject['override_class_file']))
  280. require_once(BX_DIRECTORY_PATH_ROOT . $aObject['override_class_file']);
  281. }
  282. $o = new $sClass($aObject, $oTemplate);
  283. if (!$o->_isVisibleGrid($aObject))
  284. return false;
  285. return ($GLOBALS['bxDolClasses']['BxDolGrid!'.$sObject] = $o);
  286. }
  287. public function getObject()
  288. {
  289. return $this->_sObject;
  290. }
  291. /**
  292. * Add replace markers. Curently markers are replaced in 'source' field
  293. * @param $a array of markers as key => value
  294. * @return true on success or false on error
  295. */
  296. public function addMarkers ($a)
  297. {
  298. if (empty($a) || !is_array($a))
  299. return false;
  300. $this->_aMarkers = array_merge ($this->_aMarkers, $a);
  301. return true;
  302. }
  303. public function setBrowseParams($aBrowseParams)
  304. {
  305. $this->_aBrowseParams = $aBrowseParams;
  306. $this->_aQueryAppend['bp'] = urlencode(serialize($this->_aBrowseParams));
  307. }
  308. /**
  309. * Replace provided markers in form array
  310. * @param $a form description array
  311. * @return array where markes are replaced with real values
  312. */
  313. protected function _replaceMarkers ()
  314. {
  315. $this->_aOptions['source'] = bx_replace_markers($this->_aOptions['source'], $this->_aMarkers);
  316. }
  317. protected function _getData ($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage)
  318. {
  319. $sFunc = '_getData' . $this->_aOptions['source_type'];
  320. return $this->$sFunc($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage);
  321. }
  322. protected function _getDataArray ($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage)
  323. {
  324. if ($this->_aOptions['source'] && !is_array($this->_aOptions['source'])) {
  325. $this->_aOptions['source'] = unserialize($this->_aOptions['source']);
  326. }
  327. // apply filter
  328. if ($sFilter && (!empty($this->_aOptions['filter_fields']) || !empty($this->_aOptions['filter_fields_translatable']))) {
  329. $aSource = array();
  330. foreach ($this->_aOptions['source'] as $aRow) {
  331. $bFound = false;
  332. if (!empty($this->_aOptions['filter_fields'])) {
  333. foreach ($this->_aOptions['filter_fields'] as $sField) {
  334. if (empty($aRow[$sField]) || false === stripos($aRow[$sField], $sFilter))
  335. continue;
  336. $aSource[] = $aRow;
  337. $bFound = true;
  338. break;
  339. }
  340. }
  341. if (!$bFound && !empty($this->_aOptions['filter_fields_translatable'])) {
  342. foreach ($this->_aOptions['filter_fields_translatable'] as $sField) {
  343. if (empty($aRow[$sField]) || false === stripos(_t($aRow[$sField]), $sFilter))
  344. continue;
  345. $aSource[] = $aRow;
  346. $bFound = true;
  347. break;
  348. }
  349. }
  350. }
  351. } else {
  352. $aSource = &$this->_aOptions['source'];
  353. }
  354. // sort
  355. $sSortField = false;
  356. $iSortDir = 1;
  357. if ($sOrderField && !empty($this->_aOptions['sorting_fields']) && is_array($this->_aOptions['sorting_fields']) && in_array($sOrderField, $this->_aOptions['sorting_fields'])) { // explicit order
  358. $sSortField = $sOrderField;
  359. $iSortDir = 0 === strcasecmp($sOrderDir, 'desc') ? -1 : 1;
  360. } elseif (!empty($this->_aOptions['field_order'])) { // order by "order" field
  361. $sSortField = $this->_aOptions['field_order'];
  362. }
  363. if ($sSortField) {
  364. $aSourceOrdered = $aSource;
  365. $this->_tmpOrderField = $sSortField;
  366. $this->_tmpOrderDir = $iSortDir;
  367. usort($aSourceOrdered, array($this, '_cmp'));
  368. } else {
  369. $aSourceOrdered = &$aSource;
  370. }
  371. // calculate total records count
  372. if ($this->_aOptions['show_total_count'] == 1){
  373. $this->_iTotalCount = count($aSourceOrdered);
  374. }
  375. return array_slice($aSourceOrdered, $iStart, $iPerPage, true);
  376. }
  377. protected function _getDataSql ($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage)
  378. {
  379. $oDb = BxDolDb::getInstance();
  380. $sQuery = $this->_aOptions['source'];
  381. if (false === stripos($sQuery, ' WHERE '))
  382. $sQuery .= " WHERE 1 ";
  383. $aResults = false;
  384. bx_alert('grid', 'get_data', 0, false, array('object' => $this->_sObject, 'options' => $this->_aOptions, 'markers' => $this->_aMarkers, 'filter' => $sFilter, 'browse_params' => $this->_aBrowseParams, 'results' => &$aResults));
  385. if($aResults !== false)
  386. return $aResults;
  387. // add filter condition
  388. $sOrderByFilter = '';
  389. $sQuery .= $this->_getDataSqlWhereClause($sFilter, $sOrderByFilter);
  390. // calculate total records count
  391. if ($this->_aOptions['show_total_count'] == 1){
  392. $this->_iTotalCount = $this->_getDataSqlCounter($sQuery, $sFilter);
  393. }
  394. // add order
  395. $sQuery .= $this->_getDataSqlOrderClause ($sOrderByFilter, $sOrderField, $sOrderDir);
  396. $sQuery = $sQuery . $oDb->prepareAsString(' LIMIT ?, ?', $iStart, $iPerPage);
  397. return $oDb->getAll($sQuery);
  398. }
  399. protected function _getDataSqlCounter($sQuery, $sFilter)
  400. {
  401. $oDb = BxDolDb::getInstance();
  402. $sQuery = preg_replace("/^.+FROM\s+`?" . $this->_aOptions['table'] . "`?/", "SELECT COUNT(*) FROM `" . $this->_aOptions['table'] . "`", $sQuery);
  403. if(strpos($sQuery, 'GROUP BY') === false)
  404. return $oDb->getOne($sQuery);
  405. else
  406. return array_sum($oDb->getColumn($sQuery));
  407. }
  408. protected function _getDataSqlWhereClause($sFilter, &$sOrderByFilter)
  409. {
  410. if(!$sFilter || (empty($this->_aOptions['filter_fields']) && empty($this->_aOptions['filter_fields_translatable'])))
  411. return '';
  412. $oDb = BxDolDb::getInstance();
  413. $sMode = $this->_aOptions['filter_mode'];
  414. if($sMode != 'like' && $sMode != 'fulltext')
  415. $sMode = getParam('useLikeOperator') ? 'like' : 'fulltext';
  416. $sCond = '';
  417. if('like' == $sMode) { // LIKE search
  418. // condition for regular fields
  419. if (!empty($this->_aOptions['filter_fields']))
  420. foreach ($this->_aOptions['filter_fields'] as $sField)
  421. $sCond .= $oDb->prepareAsString("`{$sField}` LIKE ? OR ", '%' . $sFilter . '%');
  422. // condition for translatable fields
  423. if (!empty($this->_aOptions['filter_fields_translatable'])) {
  424. $sCondFields = '';
  425. foreach ($this->_aOptions['filter_fields_translatable'] as $sField)
  426. $sCondFields .= "`k`.`Key` = `{$sField}` OR ";
  427. $sCondFields = rtrim($sCondFields, ' OR ');
  428. if ($sCondFields)
  429. $sCond .= $oDb->prepareAsString("(SELECT 1 FROM `sys_localization_strings` AS `s` INNER JOIN `sys_localization_keys` AS `k` ON (`k`.`ID` = `s`.`IDKey`) WHERE `s`.`string` LIKE ? AND ($sCondFields) LIMIT 1) OR ", '%' . $sFilter . '%');
  430. }
  431. $sCond = rtrim($sCond, ' OR ');
  432. }
  433. else { // FULLTEXT search
  434. // condition for regular fields
  435. if (!empty($this->_aOptions['filter_fields'])) {
  436. $sCondFields = '';
  437. foreach ($this->_aOptions['filter_fields'] as $sField)
  438. $sCondFields .= "`{$sField}`,";
  439. $sCondFields = rtrim($sCondFields, ',');
  440. if ($sCondFields) {
  441. $sCond = $oDb->prepareAsString(" MATCH ($sCondFields) AGAINST (?) ", $sFilter);
  442. $sOrderByFilter = $sCond;
  443. $sCond .= ' > 1 OR ';
  444. }
  445. }
  446. // condition for translatable fields
  447. if (!empty($this->_aOptions['filter_fields_translatable'])) {
  448. $sCondFields = '';
  449. foreach ($this->_aOptions['filter_fields_translatable'] as $sField)
  450. $sCondFields .= "`k`.`Key` = `{$sField}` OR ";
  451. $sCondFields = rtrim($sCondFields, ' OR ');
  452. if ($sCondFields)
  453. $sCond .= $oDb->prepareAsString("(SELECT 1 FROM `sys_localization_strings` AS `s` INNER JOIN `sys_localization_keys` AS `k` ON (`k`.`ID` = `s`.`IDKey`) WHERE MATCH (`s`.`string`) AGAINST (?) AND ($sCondFields) LIMIT 1) OR ", $sFilter);
  454. }
  455. $sCond = rtrim($sCond, ' OR ');
  456. }
  457. bx_alert('grid', 'get_data_by_filter', 0, false, array('object' => $this->_sObject, 'options' => $this->_aOptions, 'markers' => $this->_aMarkers, 'filter' => $sFilter, 'browse_params' => $this->_aBrowseParams, 'conditions' => &$sCond));
  458. return $sCond ? ' AND (' . $sCond . ')' : $sCond;
  459. }
  460. protected function _getDataSqlOrderClause ($sOrderByFilter, $sOrderField, $sOrderDir, $bFieldsOnly = false)
  461. {
  462. $sOrderClause = '';
  463. if ($sOrderField && is_array($this->_aOptions['sorting_fields']) && in_array($sOrderField, $this->_aOptions['sorting_fields'])) { // explicit order
  464. $sDir = (0 === strcasecmp($sOrderDir, 'desc') ? 'DESC' : 'ASC');
  465. if (is_array($this->_aOptions['sorting_fields_translatable']) && in_array($sOrderField, $this->_aOptions['sorting_fields_translatable'])) {
  466. // translatable fields
  467. $iLang = BxDolLanguages::getInstance()->getCurrentLangId();
  468. $oDb = BxDolDb::getInstance();
  469. $sOrderClause = $oDb->prepareAsString("(SELECT `s`.`string` FROM `sys_localization_strings` AS `s` INNER JOIN `sys_localization_keys` AS `k` ON (`k`.`ID` = `s`.`IDKey`) WHERE `k`.`KEY` = `$sOrderField` AND `s`.`IDLanguage` = ? LIMIT 1) ", $iLang) . $sDir;
  470. } else {
  471. // regular fields
  472. $sOrderClause = "`" . $sOrderField . "` $sDir";
  473. }
  474. } elseif ($sOrderByFilter) { // order by filter
  475. $sOrderClause = $sOrderByFilter . " DESC";
  476. } elseif (!empty($this->_aOptions['field_order'])) { // order by "order" field
  477. if (false == strpos($this->_aOptions['field_order'], ',')) {
  478. $sOrderClause = "`" . $this->_aOptions['field_order'] . "` " . $this->_sDefaultSortingOrder;
  479. } else {
  480. $a = explode(',', $this->_aOptions['field_order']);
  481. foreach ($a as $sField)
  482. $sOrderClause .= "`" . trim($sField) . "` " . $this->_sDefaultSortingOrder . ", ";
  483. if ($sOrderClause)
  484. $sOrderClause = trim($sOrderClause, ', ');
  485. }
  486. }
  487. return $bFieldsOnly || empty($sOrderClause) ? $sOrderClause : " ORDER BY " . $sOrderClause;
  488. }
  489. protected function _getCellData($sKey, $aField, $aRow)
  490. {
  491. if (isset($aRow[$sKey])) {
  492. if (!empty($aField['display'])) {
  493. bx_import('BxDolForm');
  494. $sDisplayFunc = 'display' . $aField['display'];
  495. $oDisplay = new BxDolFormCheckerHelper();
  496. return $oDisplay->$sDisplayFunc($aRow[$sKey]);
  497. } else {
  498. return bx_process_output($aRow[$sKey]);
  499. }
  500. } else {
  501. return _t('_undefined');
  502. }
  503. }
  504. protected function _cmp ($r1, $r2)
  505. {
  506. $iRet = strcasecmp($r1[$this->_tmpOrderField], $r2[$this->_tmpOrderField]);
  507. return $iRet ? $this->_tmpOrderDir * $iRet : 0;
  508. }
  509. protected function _genMethodName ($s)
  510. {
  511. return bx_gen_method_name($s);
  512. }
  513. protected function _isVisibleGrid ($a)
  514. {
  515. if (isAdmin() || !isset($a['visible_for_levels']))
  516. return true;
  517. return BxDolAcl::getInstance()->isMemberLevelInSet($a['visible_for_levels']);
  518. }
  519. }
  520. /** @} */