'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;
}
}