Marginal refactoring in AR->_findEvery().
[akelos.git] / lib / AkActionController / AkPaginator.php
blob1c814b13f2de3e6cb3f033e796a374c37a2641b4
1 <?php
2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4 // +----------------------------------------------------------------------+
5 // | Akelos Framework - http://www.akelos.org |
6 // +----------------------------------------------------------------------+
7 // | Copyright (c) 2002-2006, Akelos Media, S.L. & Bermi Ferrer Martinez |
8 // | Released under the GNU Lesser General Public License, see LICENSE.txt|
9 // +----------------------------------------------------------------------+
11 /**
12 * @package ActionController
13 * @subpackage Paginator
14 * @author Bermi Ferrer <bermi a.t akelos c.om>
15 * @copyright Copyright (c) 2002-2006, Akelos Media, S.L. http://www.akelos.org
16 * @license GNU Lesser General Public License <http://www.gnu.org/copyleft/lesser.html>
19 require_once(AK_VENDOR_DIR.DS.'pear'.DS.'HTML'.DS.'Pager'.DS.'Pager.php');
20 require_once(AK_VENDOR_DIR.DS.'pear'.DS.'HTML'.DS.'Pager'.DS.'Common.php');
22 /**
23 * AkPager and AkPaginator are a fork of Pear::Pager
24 * coded by Lorenzo Alberton <at quipo.it> and
25 * Richard Heyes <mailto:richard at phpguru.org>,
29 class AkPager extends Pager_Common
31 // {{{ AkPager()
33 /**
34 * Constructor
36 * @param mixed $options An associative array of option names
37 * and their values
38 * @access public
40 function init($options = array())
42 //set default AkPager options
43 $this->_delta = 2;
44 $this->_prevImg = '&laquo;';
45 $this->_nextImg = '&raquo;';
46 $this->_separator = '|';
47 $this->_spacesBeforeSeparator = 3;
48 $this->_spacesAfterSeparator = 3;
49 $this->_curPageSpanPre = '<b><u>';
50 $this->_curPageSpanPost = '</u></b>';
52 //set custom options
53 $err = $this->_setOptions($options);
54 if ($err !== PAGER_OK) {
55 return $this->raiseError($this->errorMessage($err), $err);
57 $this->_generatePageData();
58 $this->_setFirstLastText();
60 if ($this->_totalPages > (2 * $this->_delta + 1)) {
61 $this->links .= $this->_printFirstPage();
64 $this->links .= $this->_getBackLink();
65 $this->links .= $this->_getPageLinks();
66 $this->links .= $this->_getNextLink();
68 $this->linkTags .= $this->_getFirstLinkTag();
69 $this->linkTags .= $this->_getPrevLinkTag();
70 $this->linkTags .= $this->_getNextLinkTag();
71 $this->linkTags .= $this->_getLastLinkTag();
73 if ($this->_totalPages > (2 * $this->_delta + 1)) {
74 $this->links .= $this->_printLastPage();
78 // }}}
79 // {{{ getPageIdByOffset()
81 /**
82 * "Overload" PEAR::Pager method. VOID. Not needed here...
83 * @param integer $index Offset to get pageID for
84 * @deprecated
85 * @access public
87 function getPageIdByOffset($index=null) { }
89 // }}}
90 // {{{ getPageRangeByPageId()
92 /**
93 * Given a PageId, it returns the limits of the range of pages displayed.
94 * While getOffsetByPageId() returns the offset of the data within the
95 * current page, this method returns the offsets of the page numbers interval.
96 * E.g., if you have pageId=5 and delta=2, it will return (3, 7).
97 * PageID of 9 would give you (4, 8).
98 * If the method is called without parameter, pageID is set to currentPage#.
100 * @param integer PageID to get offsets for
101 * @return array First and last offsets
102 * @access public
104 function getPageRangeByPageId($pageid = null)
106 $pageid = isset($pageid) ? (int)$pageid : $this->_currentPage;
107 if (!isset($this->_pageData)) {
108 $this->_generatePageData();
110 if (isset($this->_pageData[$pageid]) || is_null($this->_itemData)) {
111 if ($this->_expanded) {
112 $min_surplus = ($pageid <= $this->_delta) ? ($this->_delta - $pageid + 1) : 0;
113 $max_surplus = ($pageid >= ($this->_totalPages - $this->_delta)) ?
114 ($pageid - ($this->_totalPages - $this->_delta)) : 0;
115 } else {
116 $min_surplus = $max_surplus = 0;
118 return array(
119 max($pageid - $this->_delta - $max_surplus, 1),
120 min($pageid + $this->_delta + $min_surplus, $this->_totalPages)
123 return array(0, 0);
126 // }}}
127 // {{{ getLinks()
130 * Returns back/next/first/last and page links,
131 * both as ordered and associative array.
133 * @param integer $pageID Optional pageID. If specified, links
134 * for that page are provided instead of current one.
135 * @return array back/pages/next/first/last/all links
136 * @access public
138 function getLinks($pageID = null)
140 if ($pageID != null) {
141 $_sav = $this->_currentPage;
142 $this->_currentPage = $pageID;
144 $this->links = '';
145 if ($this->_totalPages > (2 * $this->_delta + 1)) {
146 $this->links .= $this->_printFirstPage();
148 $this->links .= $this->_getBackLink();
149 $this->links .= $this->_getPageLinks();
150 $this->links .= $this->_getNextLink();
151 if ($this->_totalPages > (2 * $this->_delta + 1)) {
152 $this->links .= $this->_printLastPage();
156 $back = str_replace('&nbsp;', '', $this->_getBackLink());
157 $next = str_replace('&nbsp;', '', $this->_getNextLink());
158 $pages = $this->_getPageLinks();
159 $first = $this->_printFirstPage();
160 $last = $this->_printLastPage();
161 $all = $this->links;
162 $linkTags = $this->linkTags;
164 if ($pageID != null) {
165 $this->_currentPage = $_sav;
168 return array(
169 $back,
170 $pages,
171 trim($next),
172 $first,
173 $last,
174 $all,
175 $linkTags,
176 'back' => $back,
177 'pages' => $pages,
178 'next' => $next,
179 'first' => $first,
180 'last' => $last,
181 'all' => $all,
182 'linktags' => $linkTags
186 // }}}
187 // {{{ _getPageLinks()
190 * Returns pages link
192 * @return string Links
193 * @access private
195 function _getPageLinks($url = '')
197 //legacy setting... the preferred way to set an option now
198 //is adding it to the constuctor
199 if (!empty($url)) {
200 $this->_path = $url;
203 //If there's only one page, don't display links
204 if ($this->_clearIfVoid && ($this->_totalPages < 2)) {
205 return '';
208 $links = '';
209 if ($this->_totalPages > (2 * $this->_delta + 1)) {
210 if ($this->_expanded) {
211 if (($this->_totalPages - $this->_delta) <= $this->_currentPage) {
212 $expansion_before = $this->_currentPage - ($this->_totalPages - $this->_delta);
213 } else {
214 $expansion_before = 0;
216 for ($i = $this->_currentPage - $this->_delta - $expansion_before; $expansion_before; $expansion_before--, $i++) {
217 $print_separator_flag = ($i != $this->_currentPage + $this->_delta); // && ($i != $this->_totalPages - 1)
219 $this->range[$i] = false;
220 $this->_linkData[$this->_urlVar] = $i;
221 $links .= $this->_renderLink($this->_altPage.' '.$i, $i)
222 . $this->_spacesBefore
223 . ($print_separator_flag ? $this->_separator.$this->_spacesAfter : '');
227 $expansion_after = 0;
228 for ($i = $this->_currentPage - $this->_delta; ($i <= $this->_currentPage + $this->_delta) && ($i <= $this->_totalPages); $i++) {
229 if ($i < 1) {
230 ++$expansion_after;
231 continue;
234 // check when to print separator
235 $print_separator_flag = (($i != $this->_currentPage + $this->_delta) && ($i != $this->_totalPages));
237 if ($i == $this->_currentPage) {
238 $this->range[$i] = true;
239 $links .= $this->_curPageSpanPre . $i . $this->_curPageSpanPost;
240 } else {
241 $this->range[$i] = false;
242 $this->_linkData[$this->_urlVar] = $i;
243 $links .= $this->_renderLink($this->_altPage.' '.$i, $i);
245 $links .= $this->_spacesBefore
246 . ($print_separator_flag ? $this->_separator.$this->_spacesAfter : '');
249 if ($this->_expanded && $expansion_after) {
250 $links .= $this->_separator . $this->_spacesAfter;
251 for ($i = $this->_currentPage + $this->_delta +1; $expansion_after; $expansion_after--, $i++) {
252 $print_separator_flag = ($expansion_after != 1);
253 $this->range[$i] = false;
254 $this->_linkData[$this->_urlVar] = $i;
255 $links .= $this->_renderLink($this->_altPage.' '.$i, $i)
256 . $this->_spacesBefore
257 . ($print_separator_flag ? $this->_separator.$this->_spacesAfter : '');
261 } else {
262 //if $this->_totalPages <= (2*Delta+1) show them all
263 for ($i=1; $i<=$this->_totalPages; $i++) {
264 if ($i != $this->_currentPage) {
265 $this->range[$i] = false;
266 $this->_linkData[$this->_urlVar] = $i;
267 $links .= $this->_renderLink($this->_altPage.' '.$i, $i);
268 } else {
269 $this->range[$i] = true;
270 $links .= $this->_curPageSpanPre . $i . $this->_curPageSpanPost;
272 $links .= $this->_spacesBefore
273 . (($i != $this->_totalPages) ? $this->_separator.$this->_spacesAfter : '');
276 return $links;
279 // }}}
282 * Renders a link using the appropriate method
284 * @param altText Alternative text for this link (title property)
285 * @param linkText Text contained by this link
286 * @return string The link in string form
287 * @access private
289 function _renderLink($altText, $linkText)
291 $href = $this->controller->url_helper->modify_current_url($this->_linkData);
293 if ($this->_httpMethod == 'GET') {
295 return sprintf('<a href="%s"%s title="%s">%s</a>',
296 $href,
297 empty($this->_classString) ? '' : ' '.$this->_classString,
298 $altText,
299 $linkText
302 if ($this->_httpMethod == 'POST') {
303 return sprintf("<a href='javascript:void(0)' onClick='%s'%s title='%s'>%s</a>",
304 $this->_generateFormOnClick($this->_url, $this->_linkData),
305 empty($this->_classString) ? '' : ' '.$this->_classString,
306 $altText,
307 $linkText
310 return '';
318 * A class representing a paginator for an Active Record collection.
320 class AkPaginator
323 * Creates a new AkPaginator on the given +controller+ for a set of items
324 * of size +item_count+ and having +items_per_page+ items per page.
325 * Raises an error if items_per_page is out of bounds (i.e., less
326 * than or equal to zero). The page GET parameter for links defaults to
327 * "page" and can be overridden with +page_parameter+.
329 function &AkPaginator(&$controller, $item_count, $items_per_page, $current_page=1)
331 static $paginator;
332 if($items_per_page <= 0){
333 trigger_error(Ak::t('must have at least one item per page'),E_USER_WARNING);
336 if(empty($current_page)){
337 $current_page = 1;
338 $controller->params[$controller->_pagination_options['parameter']] = 1;
340 $this->controller =& $controller;
341 $controller_name = $controller->Request->getController();
342 $this->item_count = !empty($item_count) ? $item_count : 0;
343 $this->items_per_page = $items_per_page;
344 $this->pages = ceil($item_count/$items_per_page);
346 if(!isset($paginator[$controller_name.'_paginator'])){
348 $pager_options = array(
349 'totalItems'=>$item_count,// Number of items to page (used only if itemData is not provided).
350 'perPage'=>$items_per_page,//Number of items to display on each page.
351 'currentPage'=>$current_page,//Initial page number (if you want to show page #2 by default, set currentPage to 2)
352 'delta'=>4,// Number of page numbers to display before and after the current one.
353 'mode'=>'Sliding',// "Jumping" or "Sliding" -window - It determines pager behaviour.
354 'httpMethod'=>'GET',// Specifies the HTTP method to use. Valid values are 'GET' or 'POST'.
355 //'formID'=>'',//Specifies which HTML form to use in POST mode.
356 'importQuery'=>true,//if true (default behaviour), variables and values are imported from
357 //'extraVars'=>'',//additional URL vars to be added to the querystring.
358 //'excludeVars'=>'',//URL vars to be excluded from the querystring.
359 // the submitted data (query string) and used in the generated links, otherwise they're ignored completely
360 'expanded'=>true,// if TRUE, window size is always 2*delta+1
361 'linkClass'=> 'paginationLink',//Name of CSS class used for link styling.
362 'curPageLinkClassName'=>'paginationCurrent',//Name of CSS class used for current page link.
363 'urlVar'=>$controller->_pagination_options['parameter'],//Name of URL var used to indicate the page number. Default value is "pageID".
364 'path'=> '',//Complete path to the page (without the page name).
365 'fileName'=>'?'.$controller->_pagination_options['parameter'].'=%d',//name of the page, with a "%d" if append == TRUE.
366 'append'=>false,//If TRUE pageID is appended as GET value to the URL. If FALSE it is embedded in the URL according to fileName specs.
367 'altFirst'=>Ak::t('first page'),//Alt text to display on the link of the first page. Default value is "first page";
368 //if you want a string with the page number, use "%d" as a placeholder (for instance "page %d")
369 'altPrev'=>Ak::t('previous page'),// Alt text to display on the link of the previous page. Default value is "previous page";
370 'altNext'=>Ak::t('next page'),//Alt text to display on the link of the next page. Default value is "next page";
371 'altLast'=>Ak::t('last page'),//Alt text to display on the link of the last page. Default value is "last page";
372 //if you want a string with the page number, use "%d" as a placeholder (for instance "page %d")
373 'altPage'=>Ak::t('page').' ',//Alt text to display before the page number. Default value is "page ".
374 'prevImg'=>'<span class="paginationPrevious">'.Ak::t('previous')."</span>",//Something to display instead of "<<". It can be text such as "<< PREV" or an <img/> as well.
375 'nextImg'=>'<span class="paginationNext">'.Ak::t('next').'</span>',//Something to display instead of ">>". It can be text such as "NEXT >>" or an <img/> as well.
376 'separator'=>' ',// What to use to separate numbers. It can be an <img/>, a comma, an hyphen, or whatever.
377 'spacesBeforeSeparator'=>0,//Number of spaces before the separator.
378 'spacesAfterSeparator'=>0,//Number of spaces after the separator.
379 'firstPagePre'=>' ',//String used before first page number. It can be an <img/>, a "{", an empty string, or whatever.
380 'firstPageText'=>'<span class="paginationFirst">'.Ak::t('first').'</span>',//String used in place of first page number.
381 'firstPagePost'=>' ',//String used after first page number. It can be an <img/>, a "}", an empty string, or whatever.
382 'lastPagePre'=>' ',//Similar to firstPagePre, but used for last page number.
383 'lastPageText'=>'<span class="paginationLast">'.Ak::t('last').'</span>',//Similar to firstPageText, but used for last page number.
384 'lastPagePost'=>' ',//Similar to firstPagePost, but used for last page number.
385 'clearIfVoid'=>true,//if there's only one page, don't display pager links (returns an empty string).
386 'useSessions'=>true,//if TRUE, number of items to display per page is stored in the $_SESSION[$_sessionVar] var.
387 'closeSession'=>false,//if TRUE, the session is closed just after R/W.
388 'sessionVar'=>$controller_name.'_paginator',//Name of the session var for perPage value. A value different from default can be useful when using more than one Pager istance in the page.
389 'showAllText'=>Ak::t('show all')// Text to be used for the 'show all' option in the select box generated by getPerPageSelectBox()
392 $paginator[$controller_name.'_paginator'] =& new AkPager();
393 $paginator[$controller_name.'_paginator']->controller =& $controller;
394 $paginator[$controller_name.'_paginator']->init($pager_options);
397 $this->paginator =& $paginator[$controller_name.'_paginator'];
398 $this->controller->paginator =& $this->paginator;
400 $this->links = $this->links();
402 //$paginator[$controller_name.'_paginator']->links = 'Hola';
403 return $paginator[$controller_name.'_paginator'];
407 function &getController()
409 return $this->controller;
412 function getItemCount()
414 return $this->item_count;
417 function getItemsPerPage()
419 return $this->items_per_page;
422 function getOffset()
424 return array_shift($this->paginator->getOffsetByPageId($this->getCurrent()))-1;
427 function getCurrent()
429 return $this->paginator->getCurrentPageID();
432 function getCurrentPage()
434 return $this->paginator->getCurrentPageID();
437 function getFirstPage()
439 return 1;
441 function getFirst()
443 return 1;
446 function getLast()
448 return $this->paginator->getLastPage();
451 function pageCount()
453 return $this->paginator->numPages();
456 function lenght()
458 return $this->pageCount();
462 * Returns true if this paginator contains the page of index +number+.
464 function hasPageNumber($number)
466 return $number >= 1 && $number <= $this->pageCount();
469 function links()
471 return $this->paginator->links;