3 * @copyright Copyright (C) 2010-2022, the Friendica project
5 * @license GNU AGPL version 3 or any later version
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22 namespace Friendica\Content;
24 use Friendica\Core\L10n;
25 use Friendica\Core\Renderer;
26 use Friendica\Util\Strings;
29 * The Pager has two very different output, Minimal and Full, see renderMinimal() and renderFull() for more details.
33 /** @var int Default count of items per page */
34 const ITEMS_PER_PAGE = 50;
39 protected $itemsPerPage = self::ITEMS_PER_PAGE;
41 protected $baseQueryString = '';
47 * Instantiates a new Pager with the base parameters.
49 * Guesses the page number from the GET parameter 'page'.
52 * @param string $queryString The query string of the current page
53 * @param integer $itemsPerPage An optional number of items per page to override the default value
55 public function __construct(L10n $l10n, $queryString, $itemsPerPage = 50)
59 $this->setQueryString($queryString);
60 $this->setItemsPerPage($itemsPerPage);
61 $this->setPage(($_GET['page'] ?? 0) ?: 1);
65 * Returns the start offset for a LIMIT clause. Starts at 0.
69 public function getStart()
71 return max(0, ($this->page * $this->itemsPerPage) - $this->itemsPerPage);
75 * Returns the number of items per page
79 public function getItemsPerPage()
81 return $this->itemsPerPage;
85 * Returns the current page number
89 public function getPage()
95 * Returns the base query string.
97 * Warning: this isn't the same value as passed to the constructor.
98 * See setQueryString() for the inventory of transformations
100 * @see setBaseQuery()
103 public function getBaseQueryString()
105 return Strings::ensureQueryParameter($this->baseQueryString);
109 * Sets the number of items per page, 1 minimum.
111 * @param integer $itemsPerPage
113 public function setItemsPerPage($itemsPerPage)
115 $this->itemsPerPage = max(1, intval($itemsPerPage));
119 * Sets the current page number. Starts at 1.
121 * @param integer $page
123 public function setPage($page)
125 $this->page = max(1, intval($page));
129 * Sets the base query string from a full query string.
131 * Strips the 'page' parameter
133 * @param string $queryString
135 public function setQueryString($queryString)
137 $stripped = preg_replace('/([&?]page=[0-9]*)/', '', $queryString);
139 $stripped = trim($stripped, '/');
141 $this->baseQueryString = $stripped;
145 * Minimal pager (newer/older)
147 * This mode is intended for reverse chronological pages and presents only two links, newer (previous) and older (next).
148 * The itemCount is the number of displayed items. If no items are displayed, the older button is disabled.
152 * $pager = new Pager($a->query_string);
154 * $params = ['order' => ['sort_field' => true], 'limit' => [$pager->getStart(), $pager->getItemsPerPage()]];
155 * $items = DBA::toArray(DBA::select($table, $fields, $condition, $params));
157 * $html = $pager->renderMinimal(count($items));
159 * @param int $itemCount The number of displayed items on the page
160 * @return string HTML string of the pager
163 public function renderMinimal(int $itemCount)
165 $displayedItemCount = max(0, intval($itemCount));
170 'url' => Strings::ensureQueryParameter($this->baseQueryString . '&page=' . ($this->getPage() - 1)),
171 'text' => $this->l10n->t('newer'),
172 'class' => 'previous' . ($this->getPage() == 1 ? ' disabled' : '')
175 'url' => Strings::ensureQueryParameter($this->baseQueryString . '&page=' . ($this->getPage() + 1)),
176 'text' => $this->l10n->t('older'),
177 'class' => 'next' . ($displayedItemCount < $this->getItemsPerPage() ? ' disabled' : '')
181 $tpl = Renderer::getMarkupTemplate('paginate.tpl');
182 return Renderer::replaceMacros($tpl, ['pager' => $data]);
186 * Full pager (first / prev / 1 / 2 / ... / 14 / 15 / next / last)
188 * This mode presents page numbers as well as first, previous, next and last links.
189 * The itemCount is the total number of items including those not displayed.
193 * $total = DBA::count($table, $condition);
195 * $pager = new Pager($a->query_string, $total);
197 * $params = ['limit' => [$pager->getStart(), $pager->getItemsPerPage()]];
198 * $items = DBA::toArray(DBA::select($table, $fields, $condition, $params));
200 * $html = $pager->renderFull();
202 * @param integer $itemCount The total number of items including those note displayed on the page
203 * @return string HTML string of the pager
204 * @throws \Friendica\Network\HTTPException\InternalServerErrorException
206 public function renderFull($itemCount)
208 $totalItemCount = max(0, intval($itemCount));
212 $data['class'] = 'pagination';
213 if ($totalItemCount > $this->getItemsPerPage()) {
215 'url' => Strings::ensureQueryParameter($this->baseQueryString . '&page=1'),
216 'text' => $this->l10n->t('first'),
217 'class' => $this->getPage() == 1 ? 'disabled' : ''
220 'url' => Strings::ensureQueryParameter($this->baseQueryString . '&page=' . ($this->getPage() - 1)),
221 'text' => $this->l10n->t('prev'),
222 'class' => $this->getPage() == 1 ? 'disabled' : ''
225 $numpages = $totalItemCount / $this->getItemsPerPage();
228 $numstop = $numpages;
230 // Limit the number of displayed page number buttons.
232 $numstart = (($this->getPage() > 4) ? ($this->getPage() - 4) : 1);
233 $numstop = (($this->getPage() > ($numpages - 7)) ? $numpages : ($numstart + 8));
238 for ($i = $numstart; $i <= $numstop; $i++) {
239 if ($i == $this->getPage()) {
243 'class' => 'current active'
247 'url' => Strings::ensureQueryParameter($this->baseQueryString . '&page=' . $i),
254 if (($totalItemCount % $this->getItemsPerPage()) != 0) {
255 if ($i == $this->getPage()) {
259 'class' => 'current active'
263 'url' => Strings::ensureQueryParameter($this->baseQueryString . '&page=' . $i),
270 $data['pages'] = $pages;
272 $lastpage = (($numpages > intval($numpages)) ? intval($numpages)+1 : $numpages);
275 'url' => Strings::ensureQueryParameter($this->baseQueryString . '&page=' . ($this->getPage() + 1)),
276 'text' => $this->l10n->t('next'),
277 'class' => $this->getPage() == $lastpage ? 'disabled' : ''
280 'url' => Strings::ensureQueryParameter($this->baseQueryString . '&page=' . $lastpage),
281 'text' => $this->l10n->t('last'),
282 'class' => $this->getPage() == $lastpage ? 'disabled' : ''
286 $tpl = Renderer::getMarkupTemplate('paginate.tpl');
287 return Renderer::replaceMacros($tpl, ['pager' => $data]);