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