'table table-striped']; protected $_columns = []; protected $_templatePath = []; protected $_primaryKey = []; protected $_restParams = []; /** * @var Qs_Paginator */ protected $_paginator; protected $_paginatorUrl; protected $_paginatorFirstUrl; protected $_paginatorBaseUrl; protected $_isDisabledLimit = false; /** * @var Zend_Db_Select */ protected $_listSelect; /** * @var array */ protected $_list; protected $_listTemplate = 'list.tpl'; protected $_scriptOptions = [ /** Structure: array('name' => array('constructor' => string, 'options' => array), ...) */ 'handlers' => [], ]; /** * Adds primary key attributes to each row (ex.: '...') * @var bool */ protected $_renderPrimaryKey; /** * Allow ajax update for column "link_enum" * @var bool */ protected $_enumCell; protected $_actionToolbar = false; protected $_actionToolbarOptions = [ 'controllerAction' => null, /** * Structure: * simple: array('name' => 'title') * extended: array('name' => array('title' => string, 'attribs' => array, * 'confirmation' => String, 'confirmationCallback' => Callback compatible with qs.call)) */ 'actions' => [], 'attribs' => [ 'class' => 'list_action_toolbar', ], ]; protected $_hasRowPreview = false; protected $_rowPreviewTemplate = 'row-preview.tpl'; protected $_rowPreviewOptions = [ 'controllerAction' => 'rowPreview', 'handlerClass' => null, 'caching' => false, ]; /** * @var \Zend\EventManager\EventManagerInterface */ protected $_eventManager; public $defaultOrderBy; public $orderBy; public $idItem; public function __construct($options) { $this->setOptions($options); $this->_init(); } protected function _init() { $this->_initColumns(); $this->_initAttribs(); return $this; } public function __get($key) { if ('_' == $key[0]) { throw new Qs_ViewController_Exception(sprintf('Cannot retrieve value for protected/private property "%s"', $key)); } if ('paginator' == $key) { return $this->_getPaginator(); } if (!isset($this->$key)) { return null; } return $this->$key; } public function __set($key, $value) { $this->setOption($key, $value); } public function setOptions($options) { if (isset($options['options'])) { unset($options['options']); } foreach ($options as $key => $value) { $method = 'set' . ucfirst($key); if (method_exists($this, $method)) { $this->$method($value); } else { $this->setOption($key, $value); } } return $this; } protected function _initAttribs() { $this->_attribs['class'] .= ' ' . str_replace(['_', '\\'], '-', strtolower(get_class($this))); return $this; } public function setOption($name, $value) { $name = (string) $name; if ('_' == $name[0]) { throw new Qs_ViewController_Exception(sprintf('Invalid attribute "%s"; must not contain a leading underscore', $name)); } if (null === $value) { unset($this->$name); } else { $this->$name = $value; } return $this; } protected function _initColumns() { return $this; } public function setDefaultIpp($ipp) { $this->_defaultIpp = $ipp; return $this; } public function getDefaultIpp() { return $this->_defaultIpp; } public function getAttribs() { if (!array_key_exists('id', $this->_attribs)) { $this->_attribs['id'] = $this->getAttrib('id'); } return $this->_attribs; } public function setAttribs(array $attribs) { $this->_attribs = $attribs; return $this; } public function setAttrib($name, $value) { $this->_attribs[$name] = $value; return $this; } public function getAttrib($name) { if (!array_key_exists($name, $this->_attribs)) { if ('id' === $name) { $this->_attribs[$name] = str_replace(['_', '\\'], '-', strtolower(get_class($this))) . (($this->idItem) ? '-' . $this->idItem : ''); return $this->_attribs[$name]; } return null; } return $this->_attribs[$name]; } public function getOptions() { $options = get_object_vars($this); foreach ($options as $key => &$value) { if ('_' == substr($key, 0, 1)) { unset($options[$key]); continue; } $method = 'get' . ucfirst($key); if (method_exists($this, $method)) { $value = $this->$method(); } } return $options; } public function setSeoPaginator($flag = true) { $this->_seoPaginator = (bool) $flag; } public function addUrlVariables(array $variables) { $this->_urlVariables = array_unique(array_merge($this->_urlVariables, $variables)); return $this; } public function getUrlVariableValues(array $variableNames = null) { $result = []; if (null === $variableNames) { $variableNames = $this->_urlVariables; } foreach ($variableNames as $name) { if (null !== ($value = Qs_Request::getGetValue($name))) { $result[$name] = $value; } } if (!empty($result['ipp']) && $this->_getIpp() == $this->getDefaultIpp()) { unset($result['ipp']); } return $result; } protected function _getPaginator() { if (null === $this->_paginator) { $this->_paginator = Qs_Paginator::factory($this->getListSelect()); } return $this->_paginator; } public function getOrderBy() { $order = ''; $field = ''; if (null === $this->orderBy) { if (isset($_GET['orderBy'])) { $orderBy = $_GET['orderBy']; $orderByParams = explode(' ', $orderBy); if (count($orderByParams) >= 2) { list($field, $order) = $orderByParams; } elseif (count($orderByParams) == 1) { $field = current($orderByParams); $order = ''; } $validField = false; if (!empty($field)) { foreach ($this->_columns as $column) { if (isset($column['orderBy']) && $field == $column['orderBy']) { $validField = true; break; } } } if (!$validField) { $field = ''; foreach ($this->_columns as $column) { if (isset($column['orderBy'])) { $field = $column['orderBy']; break; } } } } if (!empty($field)) { if ('DESC' != $order) { $order = ''; } else { $order = 'DESC'; } $this->orderBy = trim("{$field} {$order}"); } else if (!empty($this->defaultOrderBy)) { $this->orderBy = $this->defaultOrderBy; } else { $this->orderBy = false; } } return $this->orderBy; } public function setPageNumber($number) { $this->_pageNumber = (int) $number; return $this; } public function parsePageNumber() { $restParamsCount = count($this->_restParams); if ($this->_seoPaginator && $restParamsCount > 1 && 'page' == $this->_restParams[$restParamsCount - 2] && is_numeric($this->_restParams[$restParamsCount - 1]) ) { return $this->_restParams[$restParamsCount - 1]; } return (int) Qs_Request::getGetValue('page', null); } public function getPageNumber() { if (null === $this->_pageNumber) { if (null === ($this->_pageNumber = $this->parsePageNumber())) { $this->_pageNumber = 1; } } return $this->_pageNumber; } public function setIpp($ipp) { $this->_ipp = $ipp; return $this; } protected function _getCookieIpp() { if (!$this->_changeableIpp) { return null; } return Qs_Request::getCookieValue($this->_getIppCookieName()); } protected function _getIppCookieName() { return 'paginator-ipp-' . constant('CURRENT_PAGE'); } protected function _getIpp() { if ($this->isDisabledLimit()) { $this->_ipp = false; } if (null === $this->_ipp) { $cookieIpp = null; if (null === ($this->_ipp = Qs_Request::getGetValue('ipp'))) { if (null === ($this->_ipp = $this->_getCookieIpp())) { $this->_ipp = $this->_defaultIpp; } else { $cookieIpp = (int) $this->_ipp; } } $this->_ipp = (int) $this->_ipp; if (!in_array($this->_ipp, $this->_ippList)) { $this->_ippList[] = $this->_ipp; sort($this->_ippList); } if ($this->_changeableIpp && $cookieIpp !== $this->_ipp) { // refresh cookie value that expires in 90 days setcookie($this->_getIppCookieName(), $this->_ipp, time() + 7776000, parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)); } } return $this->_ipp; } public function setChangeableIpp($flag) { $this->_changeableIpp = (bool) $flag; return $this; } public function getChangeableIpp() { return $this->_changeableIpp; } public function setIppList(array $list) { $this->_ippList = $list; return $this; } public function getIppList() { return $this->_ippList; } public function isDisabledLimit() { return $this->_isDisabledLimit; } public function setIsDisabledLimit($flag = true) { $this->_isDisabledLimit = (bool) $flag; return $this; } public function getLimitPage() { if ($this->isDisabledLimit()) { $this->_limitPage = false; } if (null === $this->_limitPage) { $this->_limitPage = [$this->getPageNumber(), $this->_getIpp()]; } return $this->_limitPage; } public function clearColumns() { $this->_columns = []; return $this; } public function setColumns($columns) { $this->clearColumns(); $this->addColumns($columns); return $this; } public function addColumns($columns) { if ($columns instanceof Zend_Config) { /** @var $columns Zend_Config */ $columns = $columns->toArray(); } if (!is_array($columns) || empty($columns)) { throw new Qs_ViewController_Exception('Invalid columns'); } foreach ($columns as $column) { if (!isset($column['type'])) { throw new Qs_ViewController_Exception('Undefined column type'); } if (!isset($column['name'])) { throw new Qs_ViewController_Exception('Undefined column name'); } $this->addColumn($column['type'], $column['name'], $column); } return $this; } public function addColumn($type, $name, $options = []) { $this->_columns[$name] = $this->_createColumn($type, $name, $options); return $this; } protected function _createColumn($type, $name, $options = []) { if ($options instanceof Zend_Config) { $options = $options->toArray(); } $this->_initColumnAttribs($type, $name, $options); $prepareMethod = '_prepare' . ucfirst($type) . 'Column'; if (method_exists($this, $prepareMethod)) { $this->$prepareMethod($options); } else { $this->_prepareColumn($type, $name, $options); } if (!array_key_exists('params', $options)) { $options['params'] = [$name]; } if (!array_key_exists('title', $options)) { $options['title'] = ucfirst(preg_replace('/([a-z])([A-Z])/', '$1 $2', $name)); } if (!isset($options['template'])) { $options['template'] = $type . '.tpl'; } if ('.' === dirname($options['template'])) { $options['template'] = $this->getTemplate('cells/' . $options['template']); } $options['name'] = $name; $options['type'] = $type; return $options; } public function removeColumn($name) { if (isset($this->_columns[$name])) { unset($this->_columns[$name]); } return $this; } protected function _prepareNoColumn(&$options) { if (!array_key_exists('title', $options)) { $options['title'] = '#'; } return $this; } protected function _prepareOptionsColumn(&$options) { if (!isset($options['actions'])) { $options['actions'] = ['edit' => [], 'delete' => []]; } elseif (is_string($options['actions'])) { $actions = preg_split('/; ?/', $options['actions']); $options['actions'] = array_combine($actions, array_fill(0, count($actions), [])); } elseif (is_array($options['actions']) && !Qs_Array::isAssoc($options['actions'])) { $options['actions'] = array_combine($options['actions'], array_fill(0, count($options['actions']), [])); } foreach ($options['actions'] as $action => &$actionOptions) { if (!isset($actionOptions['link'])) { $actionOptions['link'] = Qs_Request::url(['action' => $action]); } if (!isset($actionOptions['title'])) { $actionOptions['title'] = ucfirst(preg_replace('/([a-z])([A-Z])/', '$1 $2', $action)); } if (!isset($actionOptions['linkTitle'])) { $actionOptions['linkTitle'] = $actionOptions['title'] . ' this ' . $this->getConfig('itemName'); } } if (!isset($options['params'])) { $options['params'] = $this->_primaryKey; } $defaultAttribs = [ 'class' => 'grid_options', ]; if (!isset($options['attribs'])) { $options['attribs'] = []; } $options['attribs'] = array_merge($defaultAttribs, $options['attribs']); return $this; } protected function _addClassName(&$class, $newClass, $placement = 'append') { $parts = preg_split('/\s+/', $class); $parts = array_filter($parts, 'trim'); if (in_array($newClass, $parts)) { return false; } if ('append' == $placement) { $parts[] = $newClass; } else { array_unshift($parts, $newClass); } $class = implode(' ', $parts); return true; } protected function _initColumnAttribs($type, $name, &$options) { if (!array_key_exists('attribs', $options)) { $options['attribs'] = []; } if (!array_key_exists('class', $options['attribs'])) { $options['attribs']['class'] = ''; } $typeClass = strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $name)); $this->_addClassName($options['attribs']['class'], 'cell_' . $typeClass, 'prepend'); $this->_addClassName($options['attribs']['class'], 'grid_' . strtolower($type)); return $this; } protected function _prepareColumn($type, $name, &$options) { switch ($type) { case 'link_enum': if (!isset($options['key'])) { $options['key'] = $this->_primaryKey; } if (!isset($options['url'])) { $options['url'] = Qs_Request::url(['action' => 'changeOption']) . '&'; } if (!isset($options['values'])) { $options['values'] = ['y' => 'Yes', 'n' => 'No']; } $options['attribs']['data-name'] = $name; $options['attribs']['data-type'] = $type; break; default: break; } return $this; } public function setSortLink($link) { $this->_sortLink = $link; } public function getSortLink() { if (null === $this->_sortLink) { $this->_sortLink = Qs_Constant::get('BASE_URL_LANGUAGE') . '/' . Qs_Constant::get('CURRENT_PAGE') . '?'; $variableNames = array_merge($this->_urlVariables, ['page']); $queryParams = $this->getUrlVariableValues($variableNames); unset($queryParams['orderBy']); if (!empty($queryParams)) { $this->_sortLink .= http_build_query($queryParams) . '&'; } } return $this->_sortLink; } public function setPaginatorUrl($url) { $this->_paginatorUrl = $url; return $this; } public function getPaginatorBaseUrl() { if (null === $this->_paginatorBaseUrl) { $this->_paginatorBaseUrl = Qs_Constant::get('BASE_URL_LANGUAGE') . '/' . Qs_Constant::get('CURRENT_PAGE_FINAL'); if ($this->_seoPaginator && !empty($this->_restParams)) { $restParamsCount = count($this->_restParams); if ($restParamsCount > 1 && 'page' == $this->_restParams[$restParamsCount - 2] && is_numeric($this->_restParams[$restParamsCount - 1]) ) { $restParams = array_slice($this->_restParams, 0, -2); } else { $restParams = $this->_restParams; } array_walk( $restParams, function ($value, $key) use (&$restParams) { $restParams[$key] = rawurlencode($value); } ); $restParams = array_filter($restParams); if ($restParams) { $this->_paginatorBaseUrl .= '/' . implode('/', $restParams); } } } return $this->_paginatorBaseUrl; } public function setPaginatorBaseUrl($url) { $this->_paginatorBaseUrl = $url; return $this; } public function getPaginatorUrl($first = false) { $property = ($first) ? '_paginatorFirstUrl' : '_paginatorUrl'; if (null === $this->{$property}) { $queryParams = $this->getUrlVariableValues(); unset($queryParams['page']); $queryString = http_build_query($queryParams); if ($this->_seoPaginator) { $this->{$property} = $this->getPaginatorBaseUrl(); if (!$first) { $this->{$property} .= '/' . static::$_seoPaginatorPagePlaceholder; } if ($queryString) { $this->{$property} .= '?' . $queryString; } } else { $this->{$property} = Qs_Constant::get('BASE_URL_LANGUAGE') . '/' . Qs_Constant::get('CURRENT_PAGE'); if (!$first) { $queryString .= (($queryString) ? '&' : '') . 'page=' . static::$_paginatorPagePlaceholder; } if ($queryString) { $this->{$property} .= '?' . $queryString; } } } return $this->{$property}; } public function getPaginatorPagePlaceholder() { return Qs_ViewController_List::$_paginatorPagePlaceholder; } public function getColumn($name) { return Qs_Array::get($this->_columns, $name); } public function setColumn($name, $columnSpec) { $this->_columns[$name] = $columnSpec; return $this; } public function getColumns() { return $this->_columns; } public function setList(array $list) { $this->_list = $list; return $this; } public function getList() { return $this->_list; } public function toArray() { $item = $this->getOptions(); $item['config'] = $this->getConfig()->toArray(); $item['attribs'] = $this->getAttribs(); $item['sortLink'] = $this->getSortLink(); $item['list'] = $this->getList(); $item['primary'] = $this->getPrimaryKey(); $item['tpl'] = $this->getTemplate($this->_listTemplate); $scriptOptions = $this->getScriptOptions(); $specialColumnsCount = 0; $item['criticalStylesheet'] = []; if ($this->getEnumCell()) { $item['script'][] = ['js/lib/list-enum-cell.js']; $item['initObject'][] = ['qs.viewController.list.EnumCell', [['listId' => $this->getAttrib('id')]]]; } if ($this->_actionToolbar) { $specialColumnsCount += 1; $item['actionToolbar'] = $this->getActionToolbarOptions(); $colName = '_actionToolbar'; if (empty($this->_columns[$colName])) { $columnOptions = $this->_createColumn('checkbox', $colName, ['title' => '']); $this->_columns = [$colName => $columnOptions] + $this->_columns; } $this->_actionToolbarOptions['cellClasses'] = explode(' ', $this->_columns[$colName]['attribs']['class']); $scriptOptions['handlers']['actionToolbar'] = [ 'constructor' => 'Qs_ViewController_List_ActionToolbar', 'options' => $this->getActionToolbarOptions(), ]; } if ($this->_hasRowPreview) { $specialColumnsCount += 1; $item['hasRowPreview'] = $this->_hasRowPreview; $scriptOptions['handlers']['rowPreview'] = [ 'constructor' => 'Qs_ViewController_List_RowPreview', 'options' => $this->getRowPreviewOptions(), ]; } if ($scriptOptions && $scriptOptions['handlers']) { $item['script'][] = ['js/lib/list.js']; $item['initObject'][] = ['Qs_ViewController_List', [$scriptOptions]]; } $item['columns'] = $this->getColumns(); $item['columnCount'] = ($item['columns']) ? count($item['columns']) + $specialColumnsCount : 0; $item['renderPrimaryKey'] = $this->getRenderPrimaryKey(); if (!$this->isDisabledLimit()) { $this->_getPaginator()->setCurrentPageNumber($this->getPageNumber()) ->setItemCountPerPage($this->_getIpp()) ->setCurrentItems($item['list']); $item['paginator'] = get_object_vars($this->_getPaginator()->getPages()); $item['paginator']['url'] = $this->getPaginatorUrl(); $item['paginator']['firstUrl'] = $this->getPaginatorUrl(true); $item['paginator']['pagePlaceholder'] = Qs_ViewController_List::$_paginatorPagePlaceholder; $item['paginator']['ipp'] = $this->_getIpp(); $item['paginator']['changeableIpp'] = $this->getChangeableIpp(); $item['paginator']['ippList'] = $this->getIppList(); if ($item['paginator']['last'] < $this->getPageNumber() && ($item['paginator']['pageCount'] != 0 || $this->getPageNumber() > 1) ) { $this->getEventManager()->trigger( 'redirect', null, ['url' => $this->getPaginatorUrl(true), 'code' => 307] ); } if ($item['paginator']['pageCount'] > 1) { $item['criticalStylesheet'][] = 'css/components/pagination.css'; } } return $item; } /** * @param $listSelect * @return Qs_ViewController_List */ public function setListSelect(Zend_Db_Select $listSelect) { $this->_listSelect = $listSelect; return $this; } /** * @return \Zend_Db_Select */ public function getListSelect() { return $this->_listSelect; } public function getSelectOptions() { $options = []; if (false !== ($orderBy = $this->getOrderBy())) { $options['order'] = $orderBy; } if (false !== ($limitPage = $this->getLimitPage())) { $options['limitPage'] = $this->getLimitPage(); } return $options; } public function setRestParams(array $restParams) { $this->_restParams = $restParams; return $this; } public function getRestParams() { return $this->_restParams; } public function setTemplatePath(array $templatePath) { $this->_templatePath = $templatePath; return $this; } public function setPrimaryKey($primaryKey) { $this->_primaryKey = $primaryKey; return $this; } public function getPrimaryKey() { return $this->_primaryKey; } public function getTemplate($template) { return Qs_SiteMap::getTemplate($template, $this->_templatePath); } public function getViewScript($scriptName) { if (false === ($file = $this->getTemplate($scriptName))) { return $file; } $path = BASE_PATH . '/tpl'; if (0 === strpos($file, $path)) { return substr($file, strlen($path) + 1); } return $file; } public function setPreviewRowOption($name, $value) { $this->_rowPreviewOptions[$name] = $value; return $this; } public function getRowPreviewOptions() { return $this->_rowPreviewOptions; } public function setActionToolbarOption($name, $value) { $this->_actionToolbarOptions[$name] = $value; return $this; } public function getActionToolbarOptions() { if (!isset($this->_actionToolbarOptions['attribs']['id'])) { $id = str_replace(['_', '\\'], '-', strtolower(get_class($this))) . '-toolbar-' . $this->idItem; $this->_actionToolbarOptions['attribs']['id'] = $id; } return $this->_actionToolbarOptions; } public function setScriptOption($name, $value) { $this->_scriptOptions[$name] = $value; return $this; } public function getScriptOptions() { if (!isset($this->_scriptOptions['primary'])) { $this->_scriptOptions['primary'] = $this->getPrimaryKey(); } if (!isset($this->_scriptOptions['idItem']) && isset($this->idItem)) { $this->_scriptOptions['idItem'] = $this->idItem; } if (!isset($this->_scriptOptions['listId'])) { $this->_scriptOptions['listId'] = $this->getAttrib('id'); } if (!isset($this->_scriptOptions['url'])) { $this->_scriptOptions['url'] = Qs_Request::url(); } return $this->_scriptOptions; } public function renderRowPreview(array $row) { $tpl = $this->getTemplate($this->_rowPreviewTemplate); $this->getDoc()->assign('item', new Qs_Doc_Item($row)); return $this->getDoc()->fetchTemplate($tpl); } /** * @return Qs_Doc */ public function getDoc() { return Zend_Registry::get('doc'); } public function setListTemplate($listTemplate) { $this->_listTemplate = $listTemplate; return $this; } public function getListTemplate() { return $this->_listTemplate; } /** * @param EventManagerInterface $eventManager * @return $this */ public function setEventManager(EventManagerInterface $eventManager) { $this->_eventManager = $eventManager; return $this; } /** * @return \Zend\EventManager\EventManagerInterface */ public function getEventManager() { if (null === $this->_eventManager) { $this->setEventManager(new EventManager()); } return $this->_eventManager; } public function getRenderPrimaryKey() { if (null === $this->_renderPrimaryKey) { return $this->_hasRowPreview || $this->_actionToolbar; } return $this->_renderPrimaryKey; } public function setRenderPrimaryKey($renderPrimaryKey) { $this->_renderPrimaryKey = $renderPrimaryKey; return $this; } public function getEnumCell() { if (null === $this->_enumCell) { // has "link_enum" column return $this->_columns && in_array('link_enum', Qs_Array::fetchCol($this->_columns, 'type')); } return $this->_enumCell; } public function setEnumCell($cellAjaxUpdate) { $this->_enumCell = $cellAjaxUpdate; return $this; } }