/libraries/joomla/pagination/pagination.php

https://bitbucket.org/eternaware/joomus · PHP · 778 lines · 444 code · 75 blank · 259 comment · 49 complexity · b955ed2898ca1861c0027b871f8b2551 MD5 · raw file

  1. <?php
  2. /**
  3. * @package Joomla.Platform
  4. * @subpackage Pagination
  5. *
  6. * @copyright Copyright (C) 2005 - 2012 Open Source Matters, Inc. All rights reserved.
  7. * @license GNU General Public License version 2 or later; see LICENSE
  8. */
  9. defined('JPATH_PLATFORM') or die;
  10. /**
  11. * Pagination Class. Provides a common interface for content pagination for the
  12. * Joomla! Platform.
  13. *
  14. * @package Joomla.Platform
  15. * @subpackage Pagination
  16. * @since 11.1
  17. */
  18. class JPagination
  19. {
  20. /**
  21. * @var integer The record number to start displaying from.
  22. * @since 11.1
  23. */
  24. public $limitstart = null;
  25. /**
  26. * @var integer Number of rows to display per page.
  27. * @since 11.1
  28. */
  29. public $limit = null;
  30. /**
  31. * @var integer Total number of rows.
  32. * @since 11.1
  33. */
  34. public $total = null;
  35. /**
  36. * @var integer Prefix used for request variables.
  37. * @since 11.1
  38. */
  39. public $prefix = null;
  40. /**
  41. * @var integer
  42. * @since 12.2
  43. */
  44. public $pagesStart;
  45. /**
  46. * @var integer
  47. * @since 12.2
  48. */
  49. public $pagesStop;
  50. /**
  51. * @var integer
  52. * @since 12.2
  53. */
  54. public $pagesCurrent;
  55. /**
  56. * @var integer
  57. * @since 12.2
  58. */
  59. public $pagesTotal;
  60. /**
  61. * @var boolean View all flag
  62. * @since 12.1
  63. */
  64. protected $viewall = false;
  65. /**
  66. * Additional URL parameters to be added to the pagination URLs generated by the class. These
  67. * may be useful for filters and extra values when dealing with lists and GET requests.
  68. *
  69. * @var array
  70. * @since 12.1
  71. */
  72. protected $additionalUrlParams = array();
  73. /**
  74. * Constructor.
  75. *
  76. * @param integer $total The total number of items.
  77. * @param integer $limitstart The offset of the item to start at.
  78. * @param integer $limit The number of items to display per page.
  79. * @param string $prefix The prefix used for request variables.
  80. *
  81. * @since 11.1
  82. */
  83. public function __construct($total, $limitstart, $limit, $prefix = '')
  84. {
  85. // Value/type checking.
  86. $this->total = (int) $total;
  87. $this->limitstart = (int) max($limitstart, 0);
  88. $this->limit = (int) max($limit, 0);
  89. $this->prefix = $prefix;
  90. if ($this->limit > $this->total)
  91. {
  92. $this->limitstart = 0;
  93. }
  94. if (!$this->limit)
  95. {
  96. $this->limit = $total;
  97. $this->limitstart = 0;
  98. }
  99. /*
  100. * If limitstart is greater than total (i.e. we are asked to display records that don't exist)
  101. * then set limitstart to display the last natural page of results
  102. */
  103. if ($this->limitstart > $this->total - $this->limit)
  104. {
  105. $this->limitstart = max(0, (int) (ceil($this->total / $this->limit) - 1) * $this->limit);
  106. }
  107. // Set the total pages and current page values.
  108. if ($this->limit > 0)
  109. {
  110. $this->pagesTotal = ceil($this->total / $this->limit);
  111. $this->pagesCurrent = ceil(($this->limitstart + 1) / $this->limit);
  112. }
  113. // Set the pagination iteration loop values.
  114. $displayedPages = 10;
  115. $this->pagesStart = $this->pagesCurrent - ($displayedPages / 2);
  116. if ($this->pagesStart < 1)
  117. {
  118. $this->pagesStart = 1;
  119. }
  120. if ($this->pagesStart + $displayedPages > $this->pagesTotal)
  121. {
  122. $this->pagesStop = $this->pagesTotal;
  123. if ($this->pagesTotal < $displayedPages)
  124. {
  125. $this->pagesStart = 1;
  126. }
  127. else
  128. {
  129. $this->pagesStart = $this->pagesTotal - $displayedPages + 1;
  130. }
  131. }
  132. else
  133. {
  134. $this->pagesStop = $this->pagesStart + $displayedPages - 1;
  135. }
  136. // If we are viewing all records set the view all flag to true.
  137. if ($limit == 0)
  138. {
  139. $this->viewall = true;
  140. }
  141. }
  142. /**
  143. * Method to set an additional URL parameter to be added to all pagination class generated
  144. * links.
  145. *
  146. * @param string $key The name of the URL parameter for which to set a value.
  147. * @param mixed $value The value to set for the URL parameter.
  148. *
  149. * @return mixed The old value for the parameter.
  150. *
  151. * @since 11.1
  152. */
  153. public function setAdditionalUrlParam($key, $value)
  154. {
  155. // Get the old value to return and set the new one for the URL parameter.
  156. $result = isset($this->additionalUrlParams[$key]) ? $this->additionalUrlParams[$key] : null;
  157. // If the passed parameter value is null unset the parameter, otherwise set it to the given value.
  158. if ($value === null)
  159. {
  160. unset($this->additionalUrlParams[$key]);
  161. }
  162. else
  163. {
  164. $this->additionalUrlParams[$key] = $value;
  165. }
  166. return $result;
  167. }
  168. /**
  169. * Method to get an additional URL parameter (if it exists) to be added to
  170. * all pagination class generated links.
  171. *
  172. * @param string $key The name of the URL parameter for which to get the value.
  173. *
  174. * @return mixed The value if it exists or null if it does not.
  175. *
  176. * @since 11.1
  177. */
  178. public function getAdditionalUrlParam($key)
  179. {
  180. $result = isset($this->additionalUrlParams[$key]) ? $this->additionalUrlParams[$key] : null;
  181. return $result;
  182. }
  183. /**
  184. * Return the rationalised offset for a row with a given index.
  185. *
  186. * @param integer $index The row index
  187. *
  188. * @return integer Rationalised offset for a row with a given index.
  189. *
  190. * @since 11.1
  191. */
  192. public function getRowOffset($index)
  193. {
  194. return $index + 1 + $this->limitstart;
  195. }
  196. /**
  197. * Return the pagination data object, only creating it if it doesn't already exist.
  198. *
  199. * @return object Pagination data object.
  200. *
  201. * @since 11.1
  202. */
  203. public function getData()
  204. {
  205. static $data;
  206. if (!is_object($data))
  207. {
  208. $data = $this->_buildDataObject();
  209. }
  210. return $data;
  211. }
  212. /**
  213. * Create and return the pagination pages counter string, ie. Page 2 of 4.
  214. *
  215. * @return string Pagination pages counter string.
  216. *
  217. * @since 11.1
  218. */
  219. public function getPagesCounter()
  220. {
  221. $html = null;
  222. if ($this->pagesTotal > 1)
  223. {
  224. $html .= JText::sprintf('JLIB_HTML_PAGE_CURRENT_OF_TOTAL', $this->pagesCurrent, $this->pagesTotal);
  225. }
  226. return $html;
  227. }
  228. /**
  229. * Create and return the pagination result set counter string, e.g. Results 1-10 of 42
  230. *
  231. * @return string Pagination result set counter string.
  232. *
  233. * @since 11.1
  234. */
  235. public function getResultsCounter()
  236. {
  237. $html = null;
  238. $fromResult = $this->limitstart + 1;
  239. // If the limit is reached before the end of the list.
  240. if ($this->limitstart + $this->limit < $this->total)
  241. {
  242. $toResult = $this->limitstart + $this->limit;
  243. }
  244. else
  245. {
  246. $toResult = $this->total;
  247. }
  248. // If there are results found.
  249. if ($this->total > 0)
  250. {
  251. $msg = JText::sprintf('JLIB_HTML_RESULTS_OF', $fromResult, $toResult, $this->total);
  252. $html .= "\n" . $msg;
  253. }
  254. else
  255. {
  256. $html .= "\n" . JText::_('JLIB_HTML_NO_RECORDS_FOUND');
  257. }
  258. return $html;
  259. }
  260. /**
  261. * Create and return the pagination page list string, ie. Previous, Next, 1 2 3 ... x.
  262. *
  263. * @return string Pagination page list string.
  264. *
  265. * @since 11.1
  266. */
  267. public function getPagesLinks()
  268. {
  269. $app = JFactory::getApplication();
  270. // Build the page navigation list.
  271. $data = $this->_buildDataObject();
  272. $list = array();
  273. $list['prefix'] = $this->prefix;
  274. $itemOverride = false;
  275. $listOverride = false;
  276. $chromePath = JPATH_THEMES . '/' . $app->getTemplate() . '/html/pagination.php';
  277. if (file_exists($chromePath))
  278. {
  279. include_once $chromePath;
  280. if (function_exists('pagination_item_active') && function_exists('pagination_item_inactive'))
  281. {
  282. $itemOverride = true;
  283. }
  284. if (function_exists('pagination_list_render'))
  285. {
  286. $listOverride = true;
  287. }
  288. }
  289. // Build the select list
  290. if ($data->all->base !== null)
  291. {
  292. $list['all']['active'] = true;
  293. $list['all']['data'] = ($itemOverride) ? pagination_item_active($data->all) : $this->_item_active($data->all);
  294. }
  295. else
  296. {
  297. $list['all']['active'] = false;
  298. $list['all']['data'] = ($itemOverride) ? pagination_item_inactive($data->all) : $this->_item_inactive($data->all);
  299. }
  300. if ($data->start->base !== null)
  301. {
  302. $list['start']['active'] = true;
  303. $list['start']['data'] = ($itemOverride) ? pagination_item_active($data->start) : $this->_item_active($data->start);
  304. }
  305. else
  306. {
  307. $list['start']['active'] = false;
  308. $list['start']['data'] = ($itemOverride) ? pagination_item_inactive($data->start) : $this->_item_inactive($data->start);
  309. }
  310. if ($data->previous->base !== null)
  311. {
  312. $list['previous']['active'] = true;
  313. $list['previous']['data'] = ($itemOverride) ? pagination_item_active($data->previous) : $this->_item_active($data->previous);
  314. }
  315. else
  316. {
  317. $list['previous']['active'] = false;
  318. $list['previous']['data'] = ($itemOverride) ? pagination_item_inactive($data->previous) : $this->_item_inactive($data->previous);
  319. }
  320. // Make sure it exists
  321. $list['pages'] = array();
  322. foreach ($data->pages as $i => $page)
  323. {
  324. if ($page->base !== null)
  325. {
  326. $list['pages'][$i]['active'] = true;
  327. $list['pages'][$i]['data'] = ($itemOverride) ? pagination_item_active($page) : $this->_item_active($page);
  328. }
  329. else
  330. {
  331. $list['pages'][$i]['active'] = false;
  332. $list['pages'][$i]['data'] = ($itemOverride) ? pagination_item_inactive($page) : $this->_item_inactive($page);
  333. }
  334. }
  335. if ($data->next->base !== null)
  336. {
  337. $list['next']['active'] = true;
  338. $list['next']['data'] = ($itemOverride) ? pagination_item_active($data->next) : $this->_item_active($data->next);
  339. }
  340. else
  341. {
  342. $list['next']['active'] = false;
  343. $list['next']['data'] = ($itemOverride) ? pagination_item_inactive($data->next) : $this->_item_inactive($data->next);
  344. }
  345. if ($data->end->base !== null)
  346. {
  347. $list['end']['active'] = true;
  348. $list['end']['data'] = ($itemOverride) ? pagination_item_active($data->end) : $this->_item_active($data->end);
  349. }
  350. else
  351. {
  352. $list['end']['active'] = false;
  353. $list['end']['data'] = ($itemOverride) ? pagination_item_inactive($data->end) : $this->_item_inactive($data->end);
  354. }
  355. if ($this->total > $this->limit)
  356. {
  357. return ($listOverride) ? pagination_list_render($list) : $this->_list_render($list);
  358. }
  359. else
  360. {
  361. return '';
  362. }
  363. }
  364. /**
  365. * Return the pagination footer.
  366. *
  367. * @return string Pagination footer.
  368. *
  369. * @since 11.1
  370. */
  371. public function getListFooter()
  372. {
  373. $app = JFactory::getApplication();
  374. $list = array();
  375. $list['prefix'] = $this->prefix;
  376. $list['limit'] = $this->limit;
  377. $list['limitstart'] = $this->limitstart;
  378. $list['total'] = $this->total;
  379. $list['limitfield'] = $this->getLimitBox();
  380. $list['pagescounter'] = $this->getPagesCounter();
  381. $list['pageslinks'] = $this->getPagesLinks();
  382. $chromePath = JPATH_THEMES . '/' . $app->getTemplate() . '/html/pagination.php';
  383. if (file_exists($chromePath))
  384. {
  385. include_once $chromePath;
  386. if (function_exists('pagination_list_footer'))
  387. {
  388. return pagination_list_footer($list);
  389. }
  390. }
  391. return $this->_list_footer($list);
  392. }
  393. /**
  394. * Creates a dropdown box for selecting how many records to show per page.
  395. *
  396. * @return string The HTML for the limit # input box.
  397. *
  398. * @since 11.1
  399. */
  400. public function getLimitBox()
  401. {
  402. $app = JFactory::getApplication();
  403. $limits = array();
  404. // Make the option list.
  405. for ($i = 5; $i <= 30; $i += 5)
  406. {
  407. $limits[] = JHtml::_('select.option', "$i");
  408. }
  409. $limits[] = JHtml::_('select.option', '50', JText::_('J50'));
  410. $limits[] = JHtml::_('select.option', '100', JText::_('J100'));
  411. $limits[] = JHtml::_('select.option', '0', JText::_('JALL'));
  412. $selected = $this->viewall ? 0 : $this->limit;
  413. // Build the select list.
  414. if ($app->isAdmin())
  415. {
  416. $html = JHtml::_(
  417. 'select.genericlist',
  418. $limits,
  419. $this->prefix . 'limit',
  420. 'class="inputbox input-mini" size="1" onchange="Joomla.submitform();"',
  421. 'value',
  422. 'text',
  423. $selected
  424. );
  425. }
  426. else
  427. {
  428. $html = JHtml::_(
  429. 'select.genericlist',
  430. $limits,
  431. $this->prefix . 'limit',
  432. 'class="inputbox input-mini" size="1" onchange="this.form.submit()"',
  433. 'value',
  434. 'text',
  435. $selected
  436. );
  437. }
  438. return $html;
  439. }
  440. /**
  441. * Return the icon to move an item UP.
  442. *
  443. * @param integer $i The row index.
  444. * @param boolean $condition True to show the icon.
  445. * @param string $task The task to fire.
  446. * @param string $alt The image alternative text string.
  447. * @param boolean $enabled An optional setting for access control on the action.
  448. * @param string $checkbox An optional prefix for checkboxes.
  449. *
  450. * @return string Either the icon to move an item up or a space.
  451. *
  452. * @since 11.1
  453. */
  454. public function orderUpIcon($i, $condition = true, $task = 'orderup', $alt = 'JLIB_HTML_MOVE_UP', $enabled = true, $checkbox = 'cb')
  455. {
  456. if (($i > 0 || ($i + $this->limitstart > 0)) && $condition)
  457. {
  458. return JHtml::_('jgrid.orderUp', $i, $task, '', $alt, $enabled, $checkbox);
  459. }
  460. else
  461. {
  462. return '&#160;';
  463. }
  464. }
  465. /**
  466. * Return the icon to move an item DOWN.
  467. *
  468. * @param integer $i The row index.
  469. * @param integer $n The number of items in the list.
  470. * @param boolean $condition True to show the icon.
  471. * @param string $task The task to fire.
  472. * @param string $alt The image alternative text string.
  473. * @param boolean $enabled An optional setting for access control on the action.
  474. * @param string $checkbox An optional prefix for checkboxes.
  475. *
  476. * @return string Either the icon to move an item down or a space.
  477. *
  478. * @since 11.1
  479. */
  480. public function orderDownIcon($i, $n, $condition = true, $task = 'orderdown', $alt = 'JLIB_HTML_MOVE_DOWN', $enabled = true, $checkbox = 'cb')
  481. {
  482. if (($i < $n - 1 || $i + $this->limitstart < $this->total - 1) && $condition)
  483. {
  484. return JHtml::_('jgrid.orderDown', $i, $task, '', $alt, $enabled, $checkbox);
  485. }
  486. else
  487. {
  488. return '&#160;';
  489. }
  490. }
  491. /**
  492. * Create the HTML for a list footer
  493. *
  494. * @param array $list Pagination list data structure.
  495. *
  496. * @return string HTML for a list footer
  497. *
  498. * @since 11.1
  499. */
  500. protected function _list_footer($list)
  501. {
  502. $html = "<div class=\"list-footer\">\n";
  503. $html .= "\n<div class=\"limit\">" . JText::_('JGLOBAL_DISPLAY_NUM') . $list['limitfield'] . "</div>";
  504. $html .= $list['pageslinks'];
  505. $html .= "\n<div class=\"counter\">" . $list['pagescounter'] . "</div>";
  506. $html .= "\n<input type=\"hidden\" name=\"" . $list['prefix'] . "limitstart\" value=\"" . $list['limitstart'] . "\" />";
  507. $html .= "\n</div>";
  508. return $html;
  509. }
  510. /**
  511. * Create the html for a list footer
  512. *
  513. * @param array $list Pagination list data structure.
  514. *
  515. * @return string HTML for a list start, previous, next,end
  516. *
  517. * @since 11.1
  518. */
  519. protected function _list_render($list)
  520. {
  521. // Reverse output rendering for right-to-left display.
  522. $html = '<ul>';
  523. $html .= '<li class="pagination-start">' . $list['start']['data'] . '</li>';
  524. $html .= '<li class="pagination-prev">' . $list['previous']['data'] . '</li>';
  525. foreach ($list['pages'] as $page)
  526. {
  527. $html .= '<li>' . $page['data'] . '</li>';
  528. }
  529. $html .= '<li class="pagination-next">' . $list['next']['data'] . '</li>';
  530. $html .= '<li class="pagination-end">' . $list['end']['data'] . '</li>';
  531. $html .= '</ul>';
  532. return $html;
  533. }
  534. /**
  535. * Method to create an active pagination link to the item
  536. *
  537. * @param JPaginationObject $item The object with which to make an active link.
  538. *
  539. * @return string HTML link
  540. *
  541. * @since 11.1
  542. */
  543. protected function _item_active(JPaginationObject $item)
  544. {
  545. $app = JFactory::getApplication();
  546. if ($app->isAdmin())
  547. {
  548. if ($item->base > 0)
  549. {
  550. return "<a title=\"" . $item->text . "\" onclick=\"document.adminForm." . $this->prefix . "limitstart.value=" . $item->base
  551. . "; Joomla.submitform();return false;\">" . $item->text . "</a>";
  552. }
  553. else
  554. {
  555. return "<a title=\"" . $item->text . "\" onclick=\"document.adminForm." . $this->prefix
  556. . "limitstart.value=0; Joomla.submitform();return false;\">" . $item->text . "</a>";
  557. }
  558. }
  559. else
  560. {
  561. return "<a title=\"" . $item->text . "\" href=\"" . $item->link . "\" class=\"pagenav\">" . $item->text . "</a>";
  562. }
  563. }
  564. /**
  565. * Method to create an inactive pagination string
  566. *
  567. * @param JPaginationObject $item The item to be processed
  568. *
  569. * @return string
  570. *
  571. * @since 11.1
  572. */
  573. protected function _item_inactive(JPaginationObject $item)
  574. {
  575. $app = JFactory::getApplication();
  576. if ($app->isAdmin())
  577. {
  578. return "<span>" . $item->text . "</span>";
  579. }
  580. else
  581. {
  582. return "<span class=\"pagenav\">" . $item->text . "</span>";
  583. }
  584. }
  585. /**
  586. * Create and return the pagination data object.
  587. *
  588. * @return object Pagination data object.
  589. *
  590. * @since 11.1
  591. */
  592. protected function _buildDataObject()
  593. {
  594. $data = new stdClass;
  595. // Build the additional URL parameters string.
  596. $params = '';
  597. if (!empty($this->additionalUrlParams))
  598. {
  599. foreach ($this->additionalUrlParams as $key => $value)
  600. {
  601. $params .= '&' . $key . '=' . $value;
  602. }
  603. }
  604. $data->all = new JPaginationObject(JText::_('JLIB_HTML_VIEW_ALL'), $this->prefix);
  605. if (!$this->viewall)
  606. {
  607. $data->all->base = '0';
  608. $data->all->link = JRoute::_($params . '&' . $this->prefix . 'limitstart=');
  609. }
  610. // Set the start and previous data objects.
  611. $data->start = new JPaginationObject(JText::_('JLIB_HTML_START'), $this->prefix);
  612. $data->previous = new JPaginationObject(JText::_('JPREV'), $this->prefix);
  613. if ($this->pagesCurrent > 1)
  614. {
  615. $page = ($this->pagesCurrent - 2) * $this->limit;
  616. // Set the empty for removal from route
  617. // @todo remove code: $page = $page == 0 ? '' : $page;
  618. $data->start->base = '0';
  619. $data->start->link = JRoute::_($params . '&' . $this->prefix . 'limitstart=0');
  620. $data->previous->base = $page;
  621. $data->previous->link = JRoute::_($params . '&' . $this->prefix . 'limitstart=' . $page);
  622. }
  623. // Set the next and end data objects.
  624. $data->next = new JPaginationObject(JText::_('JNEXT'), $this->prefix);
  625. $data->end = new JPaginationObject(JText::_('JLIB_HTML_END'), $this->prefix);
  626. if ($this->pagesCurrent < $this->pagesTotal)
  627. {
  628. $next = $this->pagesCurrent * $this->limit;
  629. $end = ($this->pagesTotal - 1) * $this->limit;
  630. $data->next->base = $next;
  631. $data->next->link = JRoute::_($params . '&' . $this->prefix . 'limitstart=' . $next);
  632. $data->end->base = $end;
  633. $data->end->link = JRoute::_($params . '&' . $this->prefix . 'limitstart=' . $end);
  634. }
  635. $data->pages = array();
  636. $stop = $this->pagesStop;
  637. for ($i = $this->pagesStart; $i <= $stop; $i++)
  638. {
  639. $offset = ($i - 1) * $this->limit;
  640. // Set the empty for removal from route
  641. // @todo remove code: $offset = $offset == 0 ? '' : $offset;
  642. $data->pages[$i] = new JPaginationObject($i, $this->prefix);
  643. if ($i != $this->pagesCurrent || $this->viewall)
  644. {
  645. $data->pages[$i]->base = $offset;
  646. $data->pages[$i]->link = JRoute::_($params . '&' . $this->prefix . 'limitstart=' . $offset);
  647. }
  648. elseif ($i = $this->pagesCurrent)
  649. {
  650. $data->pages[$i]->active = true;
  651. }
  652. }
  653. return $data;
  654. }
  655. /**
  656. * Modifies a property of the object, creating it if it does not already exist.
  657. *
  658. * @param string $property The name of the property.
  659. * @param mixed $value The value of the property to set.
  660. *
  661. * @return void
  662. *
  663. * @since 12.2
  664. * @deprecated 13.3 Access the properties directly.
  665. */
  666. public function set($property, $value = null)
  667. {
  668. JLog::add('JPagination::set() is deprecated. Access the properties directly.', JLog::WARNING, 'deprecated');
  669. if (strpos($property, '.'))
  670. {
  671. $prop = explode('.', $property);
  672. $prop[1] = ucfirst($prop[1]);
  673. $property = implode($prop);
  674. }
  675. $this->$property = $value;
  676. }
  677. /**
  678. * Returns a property of the object or the default value if the property is not set.
  679. *
  680. * @param string $property The name of the property.
  681. * @param mixed $default The default value.
  682. *
  683. * @return mixed The value of the property.
  684. *
  685. * @since 12.2
  686. * @deprecated 13.3 Access the properties directly.
  687. */
  688. public function get($property, $default = null)
  689. {
  690. JLog::add('JPagination::get() is deprecated. Access the properties directly.', JLog::WARNING, 'deprecated');
  691. if (strpos($property, '.'))
  692. {
  693. $prop = explode('.', $property);
  694. $prop[1] = ucfirst($prop[1]);
  695. $property = implode($prop);
  696. }
  697. if (isset($this->$property))
  698. {
  699. return $this->$property;
  700. }
  701. return $default;
  702. }
  703. }