<?php
/**
* This program is licenced under the The GNU General Public License (GPL).
*
* Copyright (C) Kjetil Hårtveit 2010
*
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* 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 General Public
* License for more details.
* @license http://www.opensource.org/licenses/gpl-license.html
*
*---------------------------------------
*
* Makes pages overview out of a collection of items.
*
* Future ideas:
* @todo implement cleaner error catching?
* @todo add truncation
* @todo add customizeable page values? Instead of 1 2 3, make it possible with A B C?
*
* @author Kjetil Hårtveit <kjetil@kjetil-hartveit.com> https://old.kjetil-hartveit.com
* @version 1.2
*/
class PageLister
{
protected $allItems = array();
protected $page = 1;
protected $pageList = array();
/**
* Default options
*
* @var array
*/
protected $opts = array(
'itemsPerPage' => 5,
'urlFormat' => '%d',
'customFormatting' => false,
'urlCallback' => null,
'urlCallbackArgs' => array(),
'varPage' => '{pagenum}',
'pageLabels' => array(),
'prevAndNext' => true,
'prev' => '«',
'next' => '»'
);
/**
* <p>
* Note that you can only use either normal url formatting, custom formatting or
* callback formatting. Presedence order is: callback, custom formatting, normal formatting.
* </p>
*
* <h3>Possible options:</h3>
* <table>
* <tr>
* <th>Type</th>
* <th>Option</th>
* <th>Default</th>
* <th>Description</th>
* </tr>
* <tr>
* <td>string</td>
* <td>itemsPerPage</td>
* <td>5</td>
* <td>Number of items shown per page</td>
* </tr>
* <tr>
* <td>string</td>
* <td>urlFormat</td>
* <td>%d</td>
* <td>
* Here you can choose the format of the url. A replacement value for the pagenumber is
* required. The syntax of the replacement value and how to set it depends on different
* options.<br />
* If the <i>customFormatting</i> option is set then the value of <i>varPage</i> is used.<br />
* If the <i>urlCallback</i> option is set then at least one of the callback arguments must
* contain the value of <i>varPage</i>.<br />
* Lastly if neither of the mentioned options are set then <i>urlFormat</i> uses the sprintf
* argument syntax (ie. %d).
* </td>
* </tr>
* <tr>
* <td>boolean</td>
* <td>customFormatting</td>
* <td>false</td>
* <td>
* With custom formatting enabled you can define your own syntax for the pagenumber format.
* This is handy in case your url format accidently includes format arguments
* (like url characters encoded in utf-8) and which collides with the pagenumber argument.
* </td>
* </tr>
* <tr>
* <td>string|array</td>
* <td>urlCallback</td>
* <td>null</td>
* <td>
* If a callback is given then the pageurl will be formatted based on the result of this
* callback.
* </td>
* </tr>
* <tr>
* <td>array</td>
* <td>urlCallbackArgs</td>
* <td>array()</td>
* <td>
* If the urlCallback option is set then you can specify additional callback arguments
* with this option. <br />
* At least one of the callback arguments should contain the <i>varPage</i> value so the
* corresponding pagenumber can be injected into the callback.
* </td>
* </tr>
* <tr>
* <td>string</td>
* <td>varPage</td>
* <td>{pagenum}</td>
* <td>
* The value of this option will be replaced by the pagenumber in numerous settings.<br />
* It will be used when the option <i>customFormatting</i> is set, as well as when
* a <i>urlCallback</i> is set. In the latter case then one of the callback arguments
* should contain this value.
* </td>
* </tr>
* <tr>
* <td>array</td>
* <td>pageLabels</td>
* <td>array()</td>
* <td>Each individual page can be given a specific page label with this option. Each key in the array is the pagenumber and the value is the page label.</td>
* </tr>
* <tr>
* <td>boolean</td>
* <td>prevAndNext</td>
* <td>true</td>
* <td>Whether or not to include the "previous" and "next" items in the page list</td>
* </tr>
* <tr>
* <td>string</td>
* <td>prev</td>
* <td>«</td>
* <td>The text shown as "previous page"</td>
* </tr>
* <tr>
* <td>string</td>
* <td>next</td>
* <td>»</td>
* <td>The text shown as "next page"</td>
* </tr>
* </table>
*
* @param array $allItems All of the items which you want to generate a pagelist from
* @param int $page Current page
* @param array $opts array with options given as key-value pair.
*/
function __construct($allItems, $page, $opts=array())
{
$this->setAllItems($allItems);
$this->setPage($page);
$this->setOpts(array_merge($this->getOpts(), $opts));
}
/**
* Makes page overview
*
* <p>The page list is an associative array consisting of the following key pairs:</p>
* <table>
* <tr>
* <th>Type</th>
* <th>Key</th>
* <th>Value</th>
* </tr>
* <tr>
* <td>string</td>
* <td>url</td>
* <td>The formatted url for the page. Note that when using the <i>prevAndNext</i> option and
there is no valid previous or next page, then the url will be null. This is also the case
if a pagelink points to the current page. You can use this info to remove links that the
user should not click on.</td>
* </tr>
* <tr>
* <td>int</td>
* <td>page</td>
* <td>The pagenumber</td>
* </tr>
* <tr>
* <td>string</td>
* <td>label</td>
* <td>The page label</td>
* </tr>
* </table>
*
* @return array Page list
*/
function makePageList()
{
$page = $this->getPage();
$allItems = $this->getAllItems();
$prevAndNext = $this->getOpt('prevAndNext');
$pageLabels = $this->getOpt('pageLabels');
$itemsPerPage = $this->getOpt('itemsPerPage');
$numPages = ceil(count($allItems)/$itemsPerPage);
$pageList = array();
$prev = $page-1;
$next = $page+1;
if ($prevAndNext)
{
$pageList[] = array(
'page' => $prev,
'url' => $prev>0 ? $this->formatUrl($prev) : null,
'label' => $this->getOpt('prev')
);
}
for ($i=1; $i<=$numPages; $i++)
{
$curUrl = $page!=$i ? $this->formatUrl($i) : null;
$pageList[] = array(
'page' => $i,
'url' => $curUrl,
'label' => isset($pageLabels[$i]) ? $pageLabels[$i] : $i
);
}
if ($prevAndNext)
{
$pageList[] = array(
'page' => $next,
'url' => $next<=$numPages ? $this->formatUrl($next) : null,
'label' => $this->getOpt('next')
);
}
$this->setPageList($pageList);
return $pageList;
}
/**
* Formats url based on options
*
* @param string $format
* @param int $page
* @return string
*/
protected function formatUrl($page)
{
$res = '';
if ($this->getOpt('urlCallback')) {
$args = str_replace($this->getOpt('varPage'), $page, $this->getOpt('urlCallbackArgs'));
$res = call_user_func_array($this->getOpt('urlCallback'), $args);
} else if ($this->getOpt('customFormatting')) {
$res = str_replace($this->getOpt('varPage'), $page, $this->getOpt('urlFormat'));
} else {
$res = sprintf($this->getOpt('urlFormat'), $page);
}
return $res;
}
/**
* Returns current items
*
* @return array
*/
function getCurrentItems()
{
$allItems = $this->getAllItems();
$page = $this->getPage();
$itemsPerPage = $this->getOpt('itemsPerPage');
return array_slice($allItems, ($page-1)*$itemsPerPage, $itemsPerPage);
}
/**
* Returns a single option
*
* @param string $opt
* @return mixed
*/
function getOpt($opt)
{
$opts = $this->getOpts();
if (isset($opts[$opt]))
{
return $opts[$opt];
}
return null;
}
function setAllItems($allItems) { $this->allItems = $allItems; }
function getAllItems() { return $this->allItems; }
function setPage($page) { $this->page = $page; }
function getPage() { return $this->page; }
function setOpts($opts) { $this->opts = $opts; }
function getOpts() { return $this->opts; }
function setPageList($pageList) { $this->pageList = $pageList; }
function getPageList() { return $this->pageList; }
}
?>