<?php /* ** Copyright (C) 2001-2025 Zabbix SIA ** ** This program is free software: you can redistribute it and/or modify it under the terms of ** the GNU Affero General Public License as published by the Free Software Foundation, version 3. ** ** This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; ** without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU Affero General Public License for more details. ** ** You should have received a copy of the GNU Affero General Public License along with this program. ** If not, see <https://www.gnu.org/licenses/>. **/ /** * Pager helper for data pagination. */ class CPagerHelper { /** * Number of page buttons (use odd number). */ const RANGE = 11; /** * Create paging line based on count of data rows and trim data rows accordingly. * * @param int $page page to display * @param array $rows data rows * @param string $sort_order data sort order: ZBX_SORT_UP or ZBX_SORT_DOWN * @param CUrl $url data list URL * * @return CTag paging line */ public static function paginate($page, &$rows, $sort_order, CUrl $url) { $data = self::prepareData($page, count($rows)); $paging = self::render($data['page'], $data['num_rows'], $data['num_pages'], clone $url, $data['limit_exceeded'], $data['rows_per_page'] ); $start = ($data['page'] - 1) * $data['rows_per_page']; $end = min($data['num_rows'], $start + $data['rows_per_page']); $offset = ($sort_order == ZBX_SORT_DOWN) ? $data['offset_down'] : $data['offset_up']; // Trim given rows for the current page. $rows = array_slice($rows, $start + $offset, $end - $start, true); return $paging; } /** * Reset page number. */ public static function resetPage() { CProfile::delete('web.pager.entity'); CProfile::delete('web.pager.page'); } /** * Save page number for given entity. * * @param string $entity * @param int $page */ public static function savePage($entity, $page) { CProfile::update('web.pager.entity', $entity, PROFILE_TYPE_STR); CProfile::update('web.pager.page', $page, PROFILE_TYPE_INT); } /** * Load stored page number for given entity. * * @param string $entity * @param mixed $first_page substitute return value for the first page * * @return mixed page number (or the $first_page if wasn't stored or first page was stored) */ public static function loadPage($entity, $first_page = 1) { if ($entity !== CProfile::get('web.pager.entity')) { return $first_page; } $page = CProfile::get('web.pager.page', 1); return ($page == 1) ? $first_page : $page; } /** * Prepare data for a given page number and the number of data rows. * * @param int $page * @param int $num_rows * * @return array */ protected static function prepareData($page, $num_rows) { $rows_per_page = CWebUser::$data['rows_per_page']; $offset_down = 0; $limit_exceeded = ($num_rows > CSettingsHelper::get(CSettingsHelper::SEARCH_LIMIT)); if ($limit_exceeded) { $offset_down = $num_rows - CSettingsHelper::get(CSettingsHelper::SEARCH_LIMIT); $num_rows = CSettingsHelper::get(CSettingsHelper::SEARCH_LIMIT); } $num_pages = max(1, (int) ceil($num_rows / $rows_per_page)); $page = max(1, min($num_pages, $page)); return [ 'page' => $page, 'num_rows' => $num_rows, 'num_pages' => $num_pages, 'offset_up' => 0, 'offset_down' => $offset_down, 'rows_per_page' => $rows_per_page, 'limit_exceeded' => $limit_exceeded ]; } /** * Render paging line. * * @param int $page page number * @param int $num_rows number of rows * @param int $num_pages number of pages * @param CUrl $url data list URL * @param bool $limit_exceeded true, if data list size exceeded the configuration search limit * @param int $rows_per_page number of rows per page * * @return CTag */ protected static function render($page, $num_rows, $num_pages, CUrl $url, $limit_exceeded, $rows_per_page) { $total = $limit_exceeded ? $num_rows.'+' : $num_rows; $start = ($page - 1) * $rows_per_page; $end = min($num_rows, $start + $rows_per_page); if ($num_pages == 1) { $table_stats = _s('Displaying %1$s of %2$s found', $num_rows, $total); } else { $table_stats = _s('Displaying %1$s to %2$s of %3$s found', $start + 1, $end, $total); } return (new CDiv()) ->addClass(ZBX_STYLE_TABLE_PAGING) ->addItem( (new CTag('nav', true)) ->addClass(ZBX_STYLE_PAGING_BTN_CONTAINER) ->setAttribute('role', 'navigation') ->setAttribute('aria-label', _x('Pager', 'page navigation')) ->addItem(self::createLinks($page, $num_pages, $url)) ->addItem( (new CDiv()) ->addClass(ZBX_STYLE_TABLE_STATS) ->addItem($table_stats) ) ); } /** * Create paging tags for paging line. * * @param int $page page number * @param int $num_pages number of pages * @param CUrl $url data list URL * * @return array */ protected static function createLinks($page, $num_pages, CUrl $url) { $tags = []; if ($num_pages > 1) { $end_page = min($num_pages, max(self::RANGE, $page + floor(self::RANGE / 2))); $start_page = max(1, $end_page - self::RANGE + 1); if ($start_page > 1) { $url->removeArgument('page'); $tags[] = (new CLink(_x('First', 'page navigation'), $url->getUrl())) ->setAttribute('aria-label', _('Go to first page')); } if ($page > 1) { if ($page == 2) { $url->removeArgument('page'); } else { $url->setArgument('page', $page - 1); } $tags[] = (new CLink((new CSpan())->addClass(ZBX_STYLE_ARROW_LEFT), $url->getUrl())) ->setAttribute('aria-label', _s('Go to previous page, %1$s', $page - 1)); } for ($i = $start_page; $i <= $end_page; $i++) { if ($i == 1) { $url->removeArgument('page'); } else { $url->setArgument('page', $i); } $link = new CLink($i, $url->getUrl()); if ($i == $page) { $link ->addClass(ZBX_STYLE_PAGING_SELECTED) ->setAttribute('aria-label', _s('Go to page %1$s, current page', $i)) ->setAttribute('aria-current', 'true'); } else { $link->setAttribute('aria-label', _s('Go to page %1$s', $i)); } $tags[] = $link; } if ($page < $num_pages) { $url->setArgument('page', $page + 1); $tags[] = (new CLink((new CSpan())->addClass(ZBX_STYLE_ARROW_RIGHT), $url->getUrl())) ->setAttribute('aria-label', _s('Go to next page, %1$s', $page + 1)); } if ($end_page < $num_pages) { $url->setArgument('page', $num_pages); $tags[] = (new CLink(_x('Last', 'page navigation'), $url->getUrl())) ->setAttribute('aria-label', _s('Go to last page, %1$s', $num_pages)); } } return $tags; } }