2011-04-01 02:06:33 +02:00
|
|
|
<?php
|
|
|
|
|
2015-06-02 23:34:04 +02:00
|
|
|
final class PHUIPagerView extends AphrontView {
|
2011-04-01 02:06:33 +02:00
|
|
|
|
|
|
|
private $offset;
|
2011-04-02 18:58:19 +02:00
|
|
|
private $pageSize = 100;
|
2011-04-01 02:06:33 +02:00
|
|
|
|
|
|
|
private $count;
|
|
|
|
private $hasMorePages;
|
|
|
|
|
|
|
|
private $uri;
|
|
|
|
private $pagingParameter;
|
|
|
|
private $surroundingPages = 2;
|
2011-12-01 00:59:02 +01:00
|
|
|
private $enableKeyboardShortcuts;
|
2011-04-01 02:06:33 +02:00
|
|
|
|
2015-05-19 23:06:07 +02:00
|
|
|
public function setPageSize($page_size) {
|
2011-04-01 02:06:33 +02:00
|
|
|
$this->pageSize = max(1, $page_size);
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2015-05-19 23:06:07 +02:00
|
|
|
public function setOffset($offset) {
|
2011-04-01 02:06:33 +02:00
|
|
|
$this->offset = max(0, $offset);
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2015-05-19 23:06:07 +02:00
|
|
|
public function getOffset() {
|
2011-04-01 02:06:33 +02:00
|
|
|
return $this->offset;
|
|
|
|
}
|
|
|
|
|
2015-05-19 23:06:07 +02:00
|
|
|
public function getPageSize() {
|
2011-04-01 02:06:33 +02:00
|
|
|
return $this->pageSize;
|
|
|
|
}
|
|
|
|
|
2015-05-19 23:06:07 +02:00
|
|
|
public function setCount($count) {
|
2011-04-01 02:06:33 +02:00
|
|
|
$this->count = $count;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2015-05-19 23:06:07 +02:00
|
|
|
public function setHasMorePages($has_more) {
|
2011-04-01 02:06:33 +02:00
|
|
|
$this->hasMorePages = $has_more;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2015-05-19 23:06:07 +02:00
|
|
|
public function setURI(PhutilURI $uri, $paging_parameter) {
|
2011-04-01 02:06:33 +02:00
|
|
|
$this->uri = $uri;
|
|
|
|
$this->pagingParameter = $paging_parameter;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2015-05-19 23:06:07 +02:00
|
|
|
public function readFromRequest(AphrontRequest $request) {
|
2014-02-03 21:52:19 +01:00
|
|
|
$this->uri = $request->getRequestURI();
|
|
|
|
$this->pagingParameter = 'offset';
|
|
|
|
$this->offset = $request->getInt($this->pagingParameter);
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2015-05-19 23:06:07 +02:00
|
|
|
public function willShowPagingControls() {
|
2014-02-03 21:52:19 +01:00
|
|
|
return $this->hasMorePages;
|
|
|
|
}
|
|
|
|
|
2016-01-08 20:41:49 +01:00
|
|
|
public function getHasMorePages() {
|
|
|
|
return $this->hasMorePages;
|
|
|
|
}
|
|
|
|
|
2015-05-19 23:06:07 +02:00
|
|
|
public function setSurroundingPages($pages) {
|
2011-04-01 02:06:33 +02:00
|
|
|
$this->surroundingPages = max(0, $pages);
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function computeCount() {
|
|
|
|
if ($this->count !== null) {
|
|
|
|
return $this->count;
|
|
|
|
}
|
|
|
|
return $this->getOffset()
|
|
|
|
+ $this->getPageSize()
|
|
|
|
+ ($this->hasMorePages ? 1 : 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function isExactCountKnown() {
|
|
|
|
return $this->count !== null;
|
|
|
|
}
|
|
|
|
|
2011-05-03 02:05:22 +02:00
|
|
|
/**
|
|
|
|
* A common paging strategy is to select one extra record and use that to
|
|
|
|
* indicate that there's an additional page (this doesn't give you a
|
|
|
|
* complete page count but is often faster than counting the total number
|
|
|
|
* of items). This method will take a result array, slice it down to the
|
|
|
|
* page size if necessary, and call setHasMorePages() if there are more than
|
|
|
|
* one page of results.
|
|
|
|
*
|
|
|
|
* $results = queryfx_all(
|
|
|
|
* $conn,
|
|
|
|
* 'SELECT ... LIMIT %d, %d',
|
|
|
|
* $pager->getOffset(),
|
|
|
|
* $pager->getPageSize() + 1);
|
|
|
|
* $results = $pager->sliceResults($results);
|
|
|
|
*
|
|
|
|
* @param list Result array.
|
|
|
|
* @return list One page of results.
|
|
|
|
*/
|
|
|
|
public function sliceResults(array $results) {
|
|
|
|
if (count($results) > $this->getPageSize()) {
|
|
|
|
$results = array_slice($results, 0, $this->getPageSize(), true);
|
|
|
|
$this->setHasMorePages(true);
|
|
|
|
}
|
|
|
|
return $results;
|
|
|
|
}
|
|
|
|
|
2011-12-01 00:59:02 +01:00
|
|
|
public function setEnableKeyboardShortcuts($enable) {
|
|
|
|
$this->enableKeyboardShortcuts = $enable;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2011-04-01 02:06:33 +02:00
|
|
|
public function render() {
|
2011-04-02 18:58:19 +02:00
|
|
|
if (!$this->uri) {
|
2015-05-13 23:53:52 +02:00
|
|
|
throw new PhutilInvalidStateException('setURI');
|
2011-04-02 18:58:19 +02:00
|
|
|
}
|
2011-04-01 02:06:33 +02:00
|
|
|
|
2015-06-02 23:34:04 +02:00
|
|
|
require_celerity_resource('phui-pager-css');
|
2011-04-01 02:06:33 +02:00
|
|
|
|
|
|
|
$page = (int)floor($this->getOffset() / $this->getPageSize());
|
|
|
|
$last = ((int)ceil($this->computeCount() / $this->getPageSize())) - 1;
|
|
|
|
$near = $this->surroundingPages;
|
|
|
|
|
|
|
|
$min = $page - $near;
|
|
|
|
$max = $page + $near;
|
|
|
|
|
|
|
|
// Limit the window size to no larger than the number of available pages.
|
|
|
|
if ($max - $min > $last) {
|
|
|
|
$max = $min + $last;
|
|
|
|
if ($max == $min) {
|
2015-06-02 23:34:04 +02:00
|
|
|
return phutil_tag('div', array('class' => 'phui-pager-view'), '');
|
2011-04-01 02:06:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Slide the window so it is entirely over displayable pages.
|
|
|
|
if ($min < 0) {
|
|
|
|
$max += 0 - $min;
|
|
|
|
$min += 0 - $min;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($max > $last) {
|
|
|
|
$min -= $max - $last;
|
|
|
|
$max -= $max - $last;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Build up a list of <index, label, css-class> tuples which describe the
|
|
|
|
// links we'll display, then render them all at once.
|
|
|
|
|
|
|
|
$links = array();
|
|
|
|
|
2011-12-01 00:59:02 +01:00
|
|
|
$prev_index = null;
|
|
|
|
$next_index = null;
|
|
|
|
|
2011-04-01 02:06:33 +02:00
|
|
|
if ($min > 0) {
|
2013-03-02 00:37:32 +01:00
|
|
|
$links[] = array(0, pht('First'), null);
|
2011-04-01 02:06:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($page > 0) {
|
2013-03-02 00:37:32 +01:00
|
|
|
$links[] = array($page - 1, pht('Prev'), null);
|
2011-12-01 00:59:02 +01:00
|
|
|
$prev_index = $page - 1;
|
2011-04-01 02:06:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for ($ii = $min; $ii <= $max; $ii++) {
|
|
|
|
$links[] = array($ii, $ii + 1, ($ii == $page) ? 'current' : null);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($page < $last && $last > 0) {
|
2013-03-02 00:37:32 +01:00
|
|
|
$links[] = array($page + 1, pht('Next'), null);
|
2011-12-01 00:59:02 +01:00
|
|
|
$next_index = $page + 1;
|
2011-04-01 02:06:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($max < ($last - 1)) {
|
2013-03-02 00:37:32 +01:00
|
|
|
$links[] = array($last, pht('Last'), null);
|
2011-04-01 02:06:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
$base_uri = $this->uri;
|
|
|
|
$parameter = $this->pagingParameter;
|
2011-12-01 00:59:02 +01:00
|
|
|
|
|
|
|
if ($this->enableKeyboardShortcuts) {
|
|
|
|
$pager_links = array();
|
|
|
|
$pager_index = array(
|
|
|
|
'prev' => $prev_index,
|
|
|
|
'next' => $next_index,
|
|
|
|
);
|
|
|
|
foreach ($pager_index as $key => $index) {
|
|
|
|
if ($index !== null) {
|
|
|
|
$display_index = $this->getDisplayIndex($index);
|
|
|
|
$pager_links[$key] = (string)$base_uri->alter(
|
|
|
|
$parameter,
|
|
|
|
$display_index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Javelin::initBehavior('phabricator-keyboard-pager', $pager_links);
|
|
|
|
}
|
2011-04-01 02:06:33 +02:00
|
|
|
|
|
|
|
// Convert tuples into rendered nodes.
|
|
|
|
$rendered_links = array();
|
|
|
|
foreach ($links as $link) {
|
|
|
|
list($index, $label, $class) = $link;
|
2011-12-01 00:59:02 +01:00
|
|
|
$display_index = $this->getDisplayIndex($index);
|
2011-04-01 02:06:33 +02:00
|
|
|
$link = $base_uri->alter($parameter, $display_index);
|
2015-06-02 23:34:04 +02:00
|
|
|
$rendered_links[] = id(new PHUIButtonView())
|
|
|
|
->setTag('a')
|
|
|
|
->setHref($link)
|
2016-08-01 21:06:35 +02:00
|
|
|
->setColor(PHUIButtonView::GREY)
|
2015-06-02 23:34:04 +02:00
|
|
|
->addClass('mml')
|
|
|
|
->addClass($class)
|
|
|
|
->setText($label);
|
2011-04-01 02:06:33 +02:00
|
|
|
}
|
|
|
|
|
2013-02-13 23:50:15 +01:00
|
|
|
return phutil_tag(
|
|
|
|
'div',
|
2015-06-02 23:34:04 +02:00
|
|
|
array(
|
|
|
|
'class' => 'phui-pager-view',
|
|
|
|
),
|
2013-02-13 23:50:15 +01:00
|
|
|
$rendered_links);
|
2011-04-01 02:06:33 +02:00
|
|
|
}
|
|
|
|
|
2011-12-01 00:59:02 +01:00
|
|
|
private function getDisplayIndex($page_index) {
|
|
|
|
$page_size = $this->getPageSize();
|
|
|
|
// Use a 1-based sequence for display so that the number in the URI is
|
|
|
|
// the same as the page number you're on.
|
|
|
|
if ($page_index == 0) {
|
|
|
|
// No need for the first page to say page=1.
|
|
|
|
$display_index = null;
|
|
|
|
} else {
|
|
|
|
$display_index = $page_index * $page_size;
|
|
|
|
}
|
|
|
|
return $display_index;
|
|
|
|
}
|
|
|
|
|
2011-04-01 02:06:33 +02:00
|
|
|
}
|