_oTemplate = $oTemplate; else $this->_oTemplate = BxDolTemplate::getInstance(); $this->_oFunctions = BxTemplFunctions::getInstanceWithTemplate($this->_oTemplate); $this->_aPopupOptions = []; $this->_aQueryAppend = [ $this->_oTemplate->getCodeKey() => $this->_oTemplate->getCode() ]; $this->_aQueryAppendExclude = []; $this->_aQueryAppendExcludeApi = [$this->_oTemplate->getCodeKey()]; $this->_aQueryReset = [ $this->_aOptions['filter_get'], $this->_aOptions['order_get_field'], $this->_aOptions['order_get_dir'], $this->_aOptions['paginate_get_start'], $this->_aOptions['paginate_get_per_page'] ]; $this->_aConfirmMessages = []; } public function performActionDisplay() { if($this->_bIsApi) return $this->getCodeAPI(); require_once(BX_DIRECTORY_PATH_INC . "design.inc.php"); echoJson(array( 'grid' => $this->getCode(false), 'total_count' => $this->_iTotalCount, 'total_count_f' => $this->_getCounter() )); } public function performActionReorder() { $this->_replaceMarkers (); $aOrder = bx_get($this->_sObject . '_row'); $iOrder = 0; foreach ($aOrder as $mixedId) $this->_updateOrder($mixedId, ++$iOrder); return $this->_getActionResult([]); } public function performActionDelete() { $this->_replaceMarkers (); $aResult = []; $iAffected = 0; $aIds = bx_get('ids'); if (!$aIds || !is_array($aIds)) return $this->_getActionResult($aResult); foreach ($aIds as $mixedId) $iAffected += $this->_delete($mixedId) ? 1 : 0; if($iAffected) $aResult = !$this->_bIsApi ? ['grid' => $this->getCode(false)] : []; else $aResult = ['msg' => _t("_sys_grid_delete_failed")]; return $this->_getActionResult($aResult); } public function performActionEnable($mixedChecked = null) { $this->_replaceMarkers (); $aIds = bx_get('ids'); if (!$aIds || !is_array($aIds)) { echoJson(array()); exit; } $iChecked = (int)($mixedChecked !== null ? $mixedChecked : bx_get('checked')); $aAffectedIds = array (); foreach ($aIds as $mixedId) if ($this->_enable($mixedId, $iChecked)) $aAffectedIds[] = preg_match("/^[\d\w]+$/", $mixedId) ? $mixedId : (int)$mixedId; $sAction = $iChecked ? 'enable' : 'disable'; if($this->_bIsApi) return $aAffectedIds; echo echoJson(array($sAction => $aAffectedIds)); } public function getCode ($isDisplayHeader = true) { $this->_replaceMarkers (); if ($isDisplayHeader && empty($this->_aOptions['paginate_url'])) { // reset page query params if grid is just initialized and it uses AJAX paginate $this->resetQueryParams(); } $sPaginate = ''; $aData = array(); $sIdWrapper = 'bx-grid-wrap-' . $this->_sObject; $sIdContainer = 'bx-grid-cont-' . $this->_sObject; $sIdTable = 'bx-grid-table-' . $this->_sObject; $sFilter = $this->_getFilterValue(); $sOrderField = $this->_getOrderValue(); $sOrderDir = 0 === strcasecmp('desc', bx_get($this->_aOptions['order_get_dir'])) ? 'DESC' : 'ASC'; $iStart = 0; if($this->_aOptions['paginate_get_start'] && ($iStartGet = (int)bx_get($this->_aOptions['paginate_get_start'])) >= 0) $iStart = $iStartGet; $iPerPage = 10; if($this->_aOptions['paginate_get_per_page'] && ($iPerPageGet = (int)bx_get($this->_aOptions['paginate_get_per_page'])) > 0) $iPerPage = $iPerPageGet; else if($this->_aOptions['paginate_per_page']) $iPerPage = (int)$this->_aOptions['paginate_per_page']; if ($this->_aOptions['paginate_get_start']) { $aData = $this->_getData ($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage + 1); $sPageUrl = false; if (!empty($this->_aOptions['paginate_url'])) { $sPageUrl = $this->_aOptions['paginate_url']; $aParamsAppend = array(); if ($sFilter) { $aParamsAppend['filter'] = bx_process_input(bx_get($this->_aOptions['filter_get'])); } if ($sOrderField) { $aParamsAppend['order_field'] = bx_process_input(bx_get($this->_aOptions['order_get_field'])); $aParamsAppend['order_dir'] = bx_process_input(bx_get($this->_aOptions['order_get_dir'])); } if ($aParamsAppend) $sPageUrl = bx_append_url_params($sPageUrl, $aParamsAppend); } $aPaginateParams = array( 'start' => $iStart, 'per_page' => $iPerPage, 'page_url' => $sPageUrl ? $sPageUrl : "javascript:glGrids." . $this->_sObject . ".reload('{start}'); void(0);", ); $oPaginate = new BxTemplPaginate($aPaginateParams, $this->_oTemplate); $oPaginate->setNumFromDataArray($aData); if (isset($this->_aOptions['paginate_simple']) && false !== $this->_aOptions['paginate_simple']) $sPaginate = $oPaginate->getSimplePaginate($this->_aOptions['paginate_simple']); else $sPaginate = $oPaginate->getPaginate(); } else { $aData = $this->_getData ($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage); } if((empty($aData) || !is_array($aData)) && isset($this->_aBrowseParams['empty_message']) && !(bool)$this->_aBrowseParams['empty_message']) return ''; $sPopupOptions = '{}'; if (!empty($this->_aPopupOptions) && is_array($this->_aPopupOptions)) $sPopupOptions = json_encode($this->_aPopupOptions); $aQueryAppend = array_merge(is_array($this->_aQueryAppend) ? $this->_aQueryAppend : array(), is_array($this->_aMarkers) ? $this->_aMarkers : array()); if(!empty($this->_aQueryAppendExclude) && is_array($this->_aQueryAppendExclude)) $aQueryAppend = array_diff_key($aQueryAppend, array_flip($this->_aQueryAppendExclude)); $sQueryAppend = '{}'; if (!empty($aQueryAppend) && is_array($aQueryAppend)) $sQueryAppend = json_encode($aQueryAppend); $sConfirmMessages = '{}'; if (!empty($this->_aConfirmMessages) && is_array($this->_aConfirmMessages)) $sConfirmMessages = json_encode($this->_aConfirmMessages); $iColumns = count($this->_aOptions['fields']); $aVarsHead = $this->_getRowHead(); $sClassHeader = ''; if($this->_aOptions['show_total_count'] == 0) $sClassHeader .= ' bx-gh-no-counter'; $aVars = array ( 'object' => $this->_sObject, 'csrf_token' => BxDolForm::getCsrfToken(), 'id_table' => $sIdTable, 'id_cont' => $sIdContainer, 'id_wrap' => $sIdWrapper, 'class_table_wrapper' => $this->_aOptions['responsive'] ? 'bx-grid-table-wrapper-responsive' : '', 'sortable' => empty($this->_aOptions['field_order']) ? 0 : 1, 'sorting' => empty($this->_getOrderFields()) ? 0 : 1, 'sorting_field' => $sOrderField, 'sorting_dir' => $sOrderDir, 'bx_if:display_head' => array( 'condition' => !empty($aVarsHead), 'content' => array( 'bx_repeat:row_header' => $aVarsHead, 'columns' => $iColumns, ) ), 'bx_repeat:rows_data' => $this->_getRowsDataDesign ($aData), 'paginate_get_start' => $this->_aOptions['paginate_get_start'], 'paginate_get_per_page' => $this->_aOptions['paginate_get_per_page'], 'start' => $iStart, 'per_page' => $iPerPage, 'filter' => bx_js_string($sFilter, BX_ESCAPE_STR_APOS), 'filter_get' => bx_js_string($this->_aOptions['filter_get']), 'order_field' => bx_js_string($sOrderField, BX_ESCAPE_STR_APOS), 'order_dir' => bx_js_string($sOrderDir, BX_ESCAPE_STR_APOS), 'order_get_field' => bx_js_string($this->_aOptions['order_get_field']), 'order_get_dir' => bx_js_string($this->_aOptions['order_get_dir']), 'popup_options' => $sPopupOptions, 'query_append' => $sQueryAppend, 'confirm_messages' => $sConfirmMessages, 'columns' => $iColumns, 'bx_if:display_footer' => array( 'condition' => !empty($this->_aOptions['actions_bulk']) || !empty($sPaginate), 'content' => array( 'bx_if:actions_bulk' => array ( 'condition' => !empty($this->_aOptions['actions_bulk']), 'content' => array( 'actions_bulk' => $this->_getActions ('bulk'), ), ), 'paginate' => $sPaginate, ) ), 'bx_if:display_header' => array ( 'condition' => $isDisplayHeader, 'content' => array ( 'class' => $sClassHeader, 'bx_if:actions_independent' => array ( 'condition' => !empty($this->_aOptions['actions_independent']), 'content' => array( 'actions_independent' => $this->_getActions ('independent'), ), ), 'bx_if:filter' => array ( 'condition' => !empty($this->_aOptions['filter_fields']) || !empty($this->_aOptions['filter_fields_translatable']), 'content' => array( 'controls' => $this->_getFilterControls(), ), ), 'bx_if:counter' => array ( 'condition' => $this->_aOptions['show_total_count'] == 1, 'content' => array( 'counter' => $this->_getCounter(), ), ), ), ), ); $this->_addJsCss(); return $this->_oTemplate->parseHtmlByName('grid.html', $aVars); } /** * Get grid code API. * @return array */ public function getCodeAPI($bForceReturn = false) { $this->_replaceMarkers(); $sFilter = bx_unicode_urldecode(bx_process_input(bx_get($this->_aOptions['filter_get']))); $sOrderField = bx_unicode_urldecode(bx_process_input(bx_get($this->_aOptions['order_get_field']))); $sOrderDir = 0 === strcasecmp('desc', bx_get($this->_aOptions['order_get_dir'])) ? 'DESC' : 'ASC'; $iStart = 0; if($this->_aOptions['paginate_get_start'] && ($iStartGet = (int)bx_get($this->_aOptions['paginate_get_start'])) >= 0) $iStart = $iStartGet; $iPerPage = 10; if($this->_aOptions['paginate_get_per_page'] && ($iPerPageGet = (int)bx_get($this->_aOptions['paginate_get_per_page'])) > 0) $iPerPage = $iPerPageGet; else if($this->_aOptions['paginate_per_page']) $iPerPage = (int)$this->_aOptions['paginate_per_page']; $aData = $this->_getData($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage); if(!empty($aData) && is_array($aData)) $aData = $this->decodeDataAPI($aData); $aQueryAppend = []; if(!empty($this->_aQueryAppend) && is_array($this->_aQueryAppend)) $aQueryAppend = array_merge($aQueryAppend, $this->_aQueryAppend); if(!empty($this->_aMarkers) && is_array($this->_aMarkers)) $aQueryAppend = array_merge($aQueryAppend, $this->_aMarkers); if(!empty($this->_aQueryAppendExclude) && is_array($this->_aQueryAppendExclude)) $aQueryAppend = array_diff_key($aQueryAppend, array_flip($this->_aQueryAppendExclude)); if(!empty($this->_aQueryAppendExcludeApi) && is_array($this->_aQueryAppendExcludeApi)) $aQueryAppend = array_diff_key($aQueryAppend, array_flip($this->_aQueryAppendExcludeApi)); return [ 'settings' => [ 'object' => $this->_aOptions['object'], 'field_id' => $this->_aOptions['field_id'], 'start' => $iStart, 'per_page' => $iPerPage, 'filters' => $this->_getFilterControlsAPI(), 'query_append' => $aQueryAppend ], 'header' => $this->_getRowHeadAPI(), 'data' => defined('BX_API_PAGE') && !$bForceReturn ? [] : $aData, 'actions' => [ 'independent' => $this->_getActionsAPI('independent'), 'bulk' => $this->_getActionsAPI('bulk') ] ]; } public function decodeDataAPI($aData) { $sKeyId = $this->_aOptions['field_id']; $sMethodDefault = '_getCellDefault'; $aDataRv = []; foreach($aData as $iKey => $aRow) { $aDataRv[$iKey] = [$sKeyId => $aData[$iKey][$sKeyId]]; foreach($this->_aOptions['fields'] as $sKey => $aField) { $sMethod = '_getCell' . $this->_genMethodName($sKey); if(!method_exists($this, $sMethod)) $sMethod = $sMethodDefault; if(($aCell = $this->$sMethod(isset($aData[$iKey][$sKey]) ? $aData[$iKey][$sKey] : _t('_undefined'), $sKey, $aField, $aRow))) $aDataRv[$iKey][$sKey] = $aCell; } } return $aDataRv; } public function getFormBlockAPI($oForm, $sAction, $iId = 0) { return [ bx_api_get_block('form', $oForm->getCodeAPI(), [ 'ext' => [ 'name' => $this->_oModule->getName(), 'title' => $this->getFormBlockTitleAPI($sAction, $iId), 'request' => ['url' => $this->getFormCallBackUrlAPI($sAction, $iId), 'immutable' => true]] ] ) ]; } public function getFormBlockTitleAPI($sAction, $iId) { return ''; } public function getFormCallBackUrlAPI($sAction, $iId) { return ''; } /** * Reset query params, like filter and page number */ public function resetQueryParams() { foreach ($this->_aQueryReset as $sKey) { unset($_GET[$sKey]); unset($_POST[$sKey]); } } protected function _getRowHead() { $aRet = array(); foreach ($this->_aOptions['fields'] as $sKey => $a) { $sMethod = '_getCellHeaderDefault'; $sCustomMethod = '_getCellHeader' . $this->_genMethodName($sKey); if (method_exists($this, $sCustomMethod)) $sMethod = $sCustomMethod; $aRet[] = array('header_cell' => $this->$sMethod($sKey, $a)); } return $aRet; } protected function _getRowHeadAPI() { $bResize = false; $aHeader = []; foreach($this->_aOptions['fields'] as $sKey => $aField) { if($sKey == 'checkbox' && (int)$aField['width'] < 8) $bResize = true; $sMethod = '_getCellHeader' . $this->_genMethodName($sKey); if(($aHeaderItem = $this->{method_exists($this, $sMethod) ? $sMethod : '_getCellHeaderDefault'}($sKey, $aField))) $aHeader[] = $aHeaderItem; } if(($sKey = 'checkbox') && $bResize) $this->_resizeCellAPI($sKey, $this->_aOptions['fields'][$sKey], $aHeader); return $aHeader; } protected function _resizeCellAPI($sKey, $aField, &$aHeader) { $iMin = 8; $iAdd = $iMin - (int)$aField['width']; $iThreshold = $iMin + $iAdd; $iReduce = 0; array_walk($aHeader, function($aItem) use ($iThreshold, &$iReduce) { if((int)$aItem['width'] >= $iThreshold) $iReduce += 1; }); $fSub = round($iAdd / $iReduce, 2); array_walk($aHeader, function(&$aItem) use ($sKey, $iThreshold, $iAdd, $fSub) { if($aItem['name'] == $sKey) $aItem['width'] = (float)$aItem['width'] + $iAdd; else if((int)$aItem['width'] >= $iThreshold) $aItem['width'] = (float)$aItem['width'] - $fSub; else return; $aItem['width'] .= '%'; }); } protected function _getCellHeaderDefault ($sKey, $aField) { if($this->_bIsApi) return [ 'name' => bx_process_output($aField['name']), 'title' => bx_process_output($aField['title']), 'width' => $aField['width'] ]; $sHeader = bx_process_output($aField['title']); if (($aSortingFields = $this->_getOrderFields()) && in_array($sKey, $aSortingFields)) { $sHeader = '' . $sHeader . ''; $aField['attr_head']['bx_grid_sort_head'] = $sKey; } $sAttr = $this->_convertAttrs( $aField, 'attr_head', 'bx-def-padding-sec-bottom bx-def-padding-sec-top bx-grid-header-' . $sKey, // add default classes isset($aField['width']) ? 'width:' . $aField['width'] : false // add default styles ); return $this->_getCellHeaderWrapper ($sKey, $aField, $sHeader, $sAttr); } protected function _getCellHeaderCheckbox ($sKey, $aField) { if($this->_bIsApi) return $this->_getCellHeaderDefault($sKey, $aField); $aAttr = array( 'type' => 'checkbox', 'id' => $this->_sObject . '_check_all', 'name' => $this->_sObject . '_check_all', 'onclick' => "$('input[name=" . $this->_sObject . "_check]:not([disabled])').attr('checked', this.checked)" ); if($this->_bSelectAll) $aAttr['checked'] = 'checked'; $aField['attr'] = isset($aField['attr']) && is_array($aField['attr']) ? array_merge($aAttr, $aField['attr']) : $aAttr; $sAttrHead = $this->_convertAttrs( $aField, 'attr_head', 'bx-def-padding-sec-bottom bx-def-padding-sec-top bx-grid-header-' . $sKey, // add default classes isset($aField['width']) ? 'width:' . $aField['width'] : false // add default styles ); return $this->_getCellHeaderWrapper ($sKey, $aField, ' _convertAttrs($aField, 'attr') . ' /> ', $sAttrHead); } protected function _getCellHeaderWrapper ($sKey, $aField, $sHeader, $sAttr) { return '