Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/view/phui/PHUIPagerView.php
12249 views
1
<?php
2
3
final class PHUIPagerView extends AphrontView {
4
5
private $offset;
6
private $pageSize = 100;
7
8
private $count;
9
private $hasMorePages;
10
11
private $uri;
12
private $pagingParameter;
13
private $surroundingPages = 2;
14
private $enableKeyboardShortcuts;
15
16
public function setPageSize($page_size) {
17
$this->pageSize = max(1, $page_size);
18
return $this;
19
}
20
21
public function setOffset($offset) {
22
$this->offset = max(0, $offset);
23
return $this;
24
}
25
26
public function getOffset() {
27
return $this->offset;
28
}
29
30
public function getPageSize() {
31
return $this->pageSize;
32
}
33
34
public function setCount($count) {
35
$this->count = $count;
36
return $this;
37
}
38
39
public function setHasMorePages($has_more) {
40
$this->hasMorePages = $has_more;
41
return $this;
42
}
43
44
public function setURI(PhutilURI $uri, $paging_parameter) {
45
$this->uri = $uri;
46
$this->pagingParameter = $paging_parameter;
47
return $this;
48
}
49
50
public function readFromRequest(AphrontRequest $request) {
51
$this->uri = $request->getRequestURI();
52
$this->pagingParameter = 'offset';
53
54
$offset = $request->getInt($this->pagingParameter);
55
if ($offset === null) {
56
$offset = 0;
57
}
58
$this->offset = $offset;
59
return $this;
60
}
61
62
public function willShowPagingControls() {
63
return $this->hasMorePages || $this->getOffset();
64
}
65
66
public function getHasMorePages() {
67
return $this->hasMorePages;
68
}
69
70
public function setSurroundingPages($pages) {
71
$this->surroundingPages = max(0, $pages);
72
return $this;
73
}
74
75
private function computeCount() {
76
if ($this->count !== null) {
77
return $this->count;
78
}
79
return $this->getOffset()
80
+ $this->getPageSize()
81
+ ($this->hasMorePages ? 1 : 0);
82
}
83
84
private function isExactCountKnown() {
85
return $this->count !== null;
86
}
87
88
/**
89
* A common paging strategy is to select one extra record and use that to
90
* indicate that there's an additional page (this doesn't give you a
91
* complete page count but is often faster than counting the total number
92
* of items). This method will take a result array, slice it down to the
93
* page size if necessary, and call setHasMorePages() if there are more than
94
* one page of results.
95
*
96
* $results = queryfx_all(
97
* $conn,
98
* 'SELECT ... LIMIT %d, %d',
99
* $pager->getOffset(),
100
* $pager->getPageSize() + 1);
101
* $results = $pager->sliceResults($results);
102
*
103
* @param list Result array.
104
* @return list One page of results.
105
*/
106
public function sliceResults(array $results) {
107
if (count($results) > $this->getPageSize()) {
108
$results = array_slice($results, 0, $this->getPageSize(), true);
109
$this->setHasMorePages(true);
110
}
111
return $results;
112
}
113
114
public function setEnableKeyboardShortcuts($enable) {
115
$this->enableKeyboardShortcuts = $enable;
116
return $this;
117
}
118
119
public function render() {
120
if (!$this->uri) {
121
throw new PhutilInvalidStateException('setURI');
122
}
123
124
require_celerity_resource('phui-pager-css');
125
126
$page = (int)floor($this->getOffset() / $this->getPageSize());
127
$last = ((int)ceil($this->computeCount() / $this->getPageSize())) - 1;
128
$near = $this->surroundingPages;
129
130
$min = $page - $near;
131
$max = $page + $near;
132
133
// Limit the window size to no larger than the number of available pages.
134
if ($max - $min > $last) {
135
$max = $min + $last;
136
if ($max == $min) {
137
return phutil_tag('div', array('class' => 'phui-pager-view'), '');
138
}
139
}
140
141
// Slide the window so it is entirely over displayable pages.
142
if ($min < 0) {
143
$max += 0 - $min;
144
$min += 0 - $min;
145
}
146
147
if ($max > $last) {
148
$min -= $max - $last;
149
$max -= $max - $last;
150
}
151
152
153
// Build up a list of <index, label, css-class> tuples which describe the
154
// links we'll display, then render them all at once.
155
156
$links = array();
157
158
$prev_index = null;
159
$next_index = null;
160
161
if ($min > 0) {
162
$links[] = array(0, pht('First'), null);
163
}
164
165
if ($page > 0) {
166
$links[] = array($page - 1, pht('Prev'), null);
167
$prev_index = $page - 1;
168
}
169
170
for ($ii = $min; $ii <= $max; $ii++) {
171
$links[] = array($ii, $ii + 1, ($ii == $page) ? 'current' : null);
172
}
173
174
if ($page < $last && $last > 0) {
175
$links[] = array($page + 1, pht('Next'), null);
176
$next_index = $page + 1;
177
}
178
179
if ($max < ($last - 1)) {
180
$links[] = array($last, pht('Last'), null);
181
}
182
183
$base_uri = $this->uri;
184
$parameter = $this->pagingParameter;
185
186
if ($this->enableKeyboardShortcuts) {
187
$pager_links = array();
188
$pager_index = array(
189
'prev' => $prev_index,
190
'next' => $next_index,
191
);
192
foreach ($pager_index as $key => $index) {
193
if ($index !== null) {
194
$display_index = $this->getDisplayIndex($index);
195
196
$uri = id(clone $base_uri);
197
if ($display_index === null) {
198
$uri->removeQueryParam($parameter);
199
} else {
200
$uri->replaceQueryParam($parameter, $display_index);
201
}
202
203
$pager_links[$key] = phutil_string_cast($uri);
204
}
205
}
206
Javelin::initBehavior('phabricator-keyboard-pager', $pager_links);
207
}
208
209
// Convert tuples into rendered nodes.
210
$rendered_links = array();
211
foreach ($links as $link) {
212
list($index, $label, $class) = $link;
213
$display_index = $this->getDisplayIndex($index);
214
215
$uri = id(clone $base_uri);
216
if ($display_index === null) {
217
$uri->removeQueryParam($parameter);
218
} else {
219
$uri->replaceQueryParam($parameter, $display_index);
220
}
221
222
$rendered_links[] = id(new PHUIButtonView())
223
->setTag('a')
224
->setHref($uri)
225
->setColor(PHUIButtonView::GREY)
226
->addClass('mml')
227
->addClass($class)
228
->setText($label);
229
}
230
231
return phutil_tag(
232
'div',
233
array(
234
'class' => 'phui-pager-view',
235
),
236
$rendered_links);
237
}
238
239
private function getDisplayIndex($page_index) {
240
$page_size = $this->getPageSize();
241
// Use a 1-based sequence for display so that the number in the URI is
242
// the same as the page number you're on.
243
if ($page_index == 0) {
244
// No need for the first page to say page=1.
245
$display_index = null;
246
} else {
247
$display_index = $page_index * $page_size;
248
}
249
return $display_index;
250
}
251
252
}
253
254