icingaweb2-module-director/library/vendor/ipl/Web/Widget/Paginator.php

464 lines
11 KiB
PHP

<?php
namespace dipl\Web\Widget;
use Icinga\Exception\ProgrammingError;
use dipl\Data\Paginatable;
use dipl\Html\BaseHtmlElement;
use dipl\Html\Html;
use dipl\Html\Icon;
use dipl\Html\Link;
use dipl\Translation\TranslationHelper;
use dipl\Web\Url;
class Paginator extends BaseHtmlElement
{
use TranslationHelper;
protected $tag = 'div';
protected $defaultAttributes = [
'class' => 'pagination-control',
'role' => 'navigation',
];
/** @var Paginatable The query the paginator widget is created for */
protected $query;
/** @var int */
protected $pageCount;
/** @var int */
protected $currentCount;
/** @var Url */
protected $url;
/** @var string */
protected $pageParam;
/** @var string */
protected $perPageParam;
/** @var int */
protected $totalCount;
/** @var int */
protected $defaultItemCountPerPage = 25;
public function __construct(
Paginatable $query,
Url $url,
$pageParameter = 'page',
$perPageParameter = 'limit'
) {
$this->query = $query;
$this->setPageParam($pageParameter);
$this->setPerPageParam($perPageParameter);
$this->setUrl($url);
}
public function setItemsPerPage($count)
{
// TODO: this should become setOffset once available
$query = $this->getQuery();
$query->setLimit($count);
return $this;
}
protected function setPageParam($pageParam)
{
$this->pageParam = $pageParam;
return $this;
}
protected function setPerPageParam($perPageParam)
{
$this->perPageParam = $perPageParam;
return $this;
}
public function getPageParam()
{
return $this->pageParam;
}
public function getPerPageParam()
{
return $this->perPageParam;
}
public function getCurrentPage()
{
$query = $this->getQuery();
if ($query->hasOffset()) {
return ($query->getOffset() / $this->getItemsPerPage()) + 1;
} else {
return 1;
}
}
protected function setCurrentPage($page)
{
$page = (int) $page;
$offset = $this->firstRowOnPage($page) - 1;
if ($page > 1) {
$query = $this->getQuery();
$query->setOffset($offset);
}
}
public function getPageCount()
{
if ($this->pageCount === null) {
$this->pageCount = (int) ceil($this->getTotalItemCount() / $this->getItemsPerPage());
}
return $this->pageCount;
}
protected function getItemsPerPage()
{
$limit = $this->getQuery()->getLimit();
if ($limit === null) {
throw new ProgrammingError('Something went wrong, got no limit when there should be one');
} else {
return $limit;
}
}
public function getTotalItemCount()
{
if ($this->totalCount === null) {
$this->totalCount = count($this->getQuery());
}
return $this->totalCount;
}
public function getPrevious()
{
if ($this->hasPrevious()) {
return $this->getCurrentPage() - 1;
} else {
return null;
}
}
public function hasPrevious()
{
return $this->getCurrentPage() > 1;
}
public function getNext()
{
if ($this->hasNext()) {
return $this->getCurrentPage() + 1;
} else {
return null;
}
}
public function hasNext()
{
return $this->getCurrentPage() < $this->getPageCount();
}
public function getQuery()
{
return $this->query;
}
/**
* Returns an array of "local" pages given the page count and current page number
*
* @return array
*/
protected function getPages()
{
$page = $this->getPageCount();
$current = $this->getCurrentPage();
$range = [];
if ($page < 10) {
// Show all pages if we have less than 10
for ($i = 1; $i < 10; $i++) {
if ($i > $page) {
break;
}
$range[$i] = $i;
}
} else {
// More than 10 pages:
foreach ([1, 2] as $i) {
$range[$i] = $i;
}
if ($current < 6) {
// We are on page 1-5 from
for ($i = 1; $i <= 7; $i++) {
$range[$i] = $i;
}
} else {
// Current page > 5
$range[] = '…';
if (($page - $current) < 5) {
// Less than 5 pages left
$start = 5 - ($page - $current);
} else {
$start = 1;
}
for ($i = $current - $start; $i < ($current + (4 - $start)); $i++) {
if ($i > $page) {
break;
}
$range[$i] = $i;
}
}
if ($current < ($page - 2)) {
$range[] = '…';
}
foreach ([$page - 1, $page] as $i) {
$range[$i] = $i;
}
}
if (empty($range)) {
$range[] = 1;
}
return $range;
}
public function getDefaultItemCountPerPage()
{
return $this->defaultItemCountPerPage;
}
public function setDefaultItemCountPerPage($count)
{
$this->defaultItemCountPerPage = (int) $count;
return $this;
}
public function setUrl(Url $url)
{
$page = (int) $url->shift($this->getPageParam());
$perPage = (int) $url->getParam($this->getPerPageParam());
if ($perPage > 0) {
$this->setItemsPerPage($perPage);
} else {
if (! $this->getQuery()->hasLimit()) {
$this->setItemsPerPage($this->getDefaultItemCountPerPage());
}
}
if ($page > 0) {
$this->setCurrentPage($page);
}
$this->url = $url;
return $this;
}
public function getUrl()
{
if ($this->url === null) {
$this->setUrl(Url::fromRequest());
}
return $this->url;
}
public function getPreviousLabel()
{
return $this->getLabel($this->getCurrentPage() - 1);
}
protected function getNextLabel()
{
return $this->getLabel($this->getCurrentPage() + 1);
}
protected function getLabel($page)
{
return sprintf(
$this->translate('Show rows %u to %u of %u'),
$this->firstRowOnPage($page),
$this->lastRowOnPage($page),
$this->getTotalItemCount()
);
}
protected function renderPrevious()
{
return Html::tag('li', [
'class' => 'nav-item'
], Link::create(
Icon::create('angle-double-left'),
$this->makeUrl($this->getPrevious()),
null,
[
'title' => $this->getPreviousLabel(),
'class' => 'previous-page'
]
));
}
protected function renderNoPrevious()
{
return $this->renderDisabled(Html::tag('span', [
'class' => 'previous-page'
], [
$this->srOnly($this->translate('Previous page')),
Icon::create('angle-double-left')
]));
}
protected function renderNext()
{
return Html::tag('li', [
'class' => 'nav-item'
], Link::create(
Icon::create('angle-double-right'),
$this->makeUrl($this->getNext()),
null,
[
'title' => $this->getNextLabel(),
'class' => 'next-page'
]
));
}
protected function renderNoNext()
{
return $this->renderDisabled(Html::tag('span', [
'class' => 'previous-page'
], [
$this->srOnly($this->translate('Next page')),
Icon::create('angle-double-right')
]));
}
protected function renderDots()
{
return $this->renderDisabled(Html::tag('span', null, '…'));
}
protected function renderInnerPages()
{
$pages = [];
$current = $this->getCurrentPage();
foreach ($this->getPages() as $page) {
if ($page === '…') {
$pages[] = $this->renderDots();
} else {
$pages[] = Html::tag(
'li',
$page === $current ? ['class' => 'active'] : null,
$this->makeLink($page)
);
}
}
return $pages;
}
protected function lastRowOnPage($page)
{
$perPage = $this->getItemsPerPage();
$total = $this->getTotalItemCount();
$last = $page * $perPage;
if ($last > $total) {
$last = $total;
}
return $last;
}
protected function firstRowOnPage($page)
{
return ($page - 1) * $this->getItemsPerPage() + 1;
}
protected function makeLink($page)
{
return Link::create(
$page,
$this->makeUrl($page),
null,
['title' => $this->getLabel($page)]
);
}
protected function makeUrl($page)
{
if ($page) {
return $this->getUrl()->with('page', $page);
} else {
return $this->getUrl();
}
}
protected function srOnly($content)
{
return Html::tag('span', ['class' => 'sr-only'], $content);
}
protected function renderDisabled($content)
{
return Html::tag('li', [
'class' => ['nav-item', 'disabled'],
'aria-hidden' => 'true'
], $content);
}
protected function renderList()
{
return Html::tag(
'ul',
['class' => ['nav', 'tab-nav']],
[
$this->hasPrevious() ? $this->renderPrevious() : $this->renderNoPrevious(),
$this->renderInnerPages(),
$this->hasNext() ? $this->renderNext() : $this->renderNoNext()
]
);
}
public function assemble()
{
$this->add([
$this->renderScreenReaderHeader(),
$this->renderList()
]);
}
protected function renderScreenReaderHeader()
{
return Html::tag('h2', [
// 'id' => $this->protectId('pagination') -> why?
'class' => 'sr-only',
'tab-index' => '-1'
], $this->translate('Pagination'));
}
public function render()
{
if ($this->getPageCount() < 2) {
return '';
} else {
return parent::render();
}
}
}