From ee0cc6aac5a9402e8983780dc608955c22e08b7e Mon Sep 17 00:00:00 2001 From: Deon George Date: Sat, 27 Aug 2011 17:14:02 +1000 Subject: [PATCH] Added Kohana Pagination https://github.com/awellis13/pagination --- application/bootstrap.php | 1 + includes/kohana/modules/pagination/.gitignore | 1 + .../pagination/classes/kohana/pagination.php | 335 ++++++++++++++++++ .../modules/pagination/classes/pagination.php | 3 + .../modules/pagination/config/pagination.php | 15 + .../modules/pagination/config/userguide.php | 23 ++ .../pagination/guide/pagination/config.md | 94 +++++ .../pagination/guide/pagination/examples.md | 0 .../pagination/guide/pagination/index.md | 0 .../pagination/guide/pagination/menu.md | 4 + .../pagination/guide/pagination/usage.md | 0 .../pagination/views/pagination/basic.php | 37 ++ .../pagination/views/pagination/floating.php | 94 +++++ 13 files changed, 607 insertions(+) create mode 100644 includes/kohana/modules/pagination/.gitignore create mode 100644 includes/kohana/modules/pagination/classes/kohana/pagination.php create mode 100644 includes/kohana/modules/pagination/classes/pagination.php create mode 100644 includes/kohana/modules/pagination/config/pagination.php create mode 100644 includes/kohana/modules/pagination/config/userguide.php create mode 100644 includes/kohana/modules/pagination/guide/pagination/config.md create mode 100644 includes/kohana/modules/pagination/guide/pagination/examples.md create mode 100644 includes/kohana/modules/pagination/guide/pagination/index.md create mode 100644 includes/kohana/modules/pagination/guide/pagination/menu.md create mode 100644 includes/kohana/modules/pagination/guide/pagination/usage.md create mode 100644 includes/kohana/modules/pagination/views/pagination/basic.php create mode 100644 includes/kohana/modules/pagination/views/pagination/floating.php diff --git a/application/bootstrap.php b/application/bootstrap.php index 974beed3..4d4a646e 100644 --- a/application/bootstrap.php +++ b/application/bootstrap.php @@ -111,6 +111,7 @@ Kohana::modules(array( 'userguide' => SMDPATH.'userguide', // User guide and API documentation 'khemail' => SMDPATH.'khemail', // Email module for Kohana 3 PHP Framework 'gchart' => MODPATH.'gchart', // Google Chart Module + 'pagination' => SMDPATH.'pagination', // Kohana Pagination module for Kohana 3 PHP Framework 'xml' => SMDPATH.'xml', // XML module for Kohana 3 PHP Framework )); diff --git a/includes/kohana/modules/pagination/.gitignore b/includes/kohana/modules/pagination/.gitignore new file mode 100644 index 00000000..496ee2ca --- /dev/null +++ b/includes/kohana/modules/pagination/.gitignore @@ -0,0 +1 @@ +.DS_Store \ No newline at end of file diff --git a/includes/kohana/modules/pagination/classes/kohana/pagination.php b/includes/kohana/modules/pagination/classes/kohana/pagination.php new file mode 100644 index 00000000..56b10d01 --- /dev/null +++ b/includes/kohana/modules/pagination/classes/kohana/pagination.php @@ -0,0 +1,335 @@ + array('source' => 'query_string', 'key' => 'page'), + 'total_items' => 0, + 'items_per_page' => 10, + 'view' => 'pagination/basic', + 'auto_hide' => TRUE, + 'first_page_in_url' => FALSE, + ); + + /** + * @var array Members that have access methods + */ + protected $_properties = array( + 'current_page', 'total_items', 'items_per_page', 'total_pages', 'current_first_item', 'current_last_item', + 'previous_page', 'next_page', 'first_page', 'last_page', 'offset', + ); + + // Current page number + protected $_current_page; + + // Total item count + protected $_total_items; + + // How many items to show per page + protected $_items_per_page; + + // Total page count + protected $_total_pages; + + // Item offset for the first item displayed on the current page + protected $_current_first_item; + + // Item offset for the last item displayed on the current page + protected $_current_last_item; + + // Previous page number; FALSE if the current page is the first one + protected $_previous_page; + + // Next page number; FALSE if the current page is the last one + protected $_next_page; + + // First page number; FALSE if the current page is the first one + protected $_first_page; + + // Last page number; FALSE if the current page is the last one + protected $_last_page; + + // Query offset + protected $_offset; + + /** + * Creates a new Pagination object. + * + * @param array configuration + * @return Pagination + */ + public static function factory(array $config = array()) + { + return new Pagination($config); + } + + /** + * Creates a new Pagination object. + * + * @param array configuration + * @return void + */ + public function __construct(array $config = array()) + { + // Overwrite system defaults with application defaults + $this->config = $this->config_group() + $this->config; + + // Pagination setup + $this->setup($config); + } + + /** + * Retrieves a pagination config group from the config file. One config group can + * refer to another as its parent, which will be recursively loaded. + * + * @param string pagination config group; "default" if none given + * @return array config settings + */ + public function config_group($group = 'default') + { + // Load the pagination config file + $config_file = Kohana::config('pagination'); + + // Initialize the $config array + $config['group'] = (string) $group; + + // Recursively load requested config groups + while (isset($config['group']) AND isset($config_file->$config['group'])) + { + // Temporarily store config group name + $group = $config['group']; + unset($config['group']); + + // Add config group values, not overwriting existing keys + $config += $config_file->$group; + } + + // Get rid of possible stray config group names + unset($config['group']); + + // Return the merged config group settings + return $config; + } + + /** + * Loads configuration settings into the object and (re)calculates pagination if needed. + * Allows you to update config settings after a Pagination object has been constructed. + * + * @param array configuration + * @return object Pagination + */ + public function setup(array $config = array()) + { + if (isset($config['group'])) + { + // Recursively load requested config groups + $config += $this->config_group($config['group']); + } + + // Overwrite the current config settings + $this->config = $config + $this->config; + + // Only (re)calculate pagination when needed + if ($this->_current_page === NULL + OR isset($config['current_page']) + OR isset($config['total_items']) + OR isset($config['items_per_page'])) + { + // Retrieve the current page number + if ( ! empty($this->config['current_page']['page'])) + { + // The current page number has been set manually + $this->_current_page = (int) $this->config['current_page']['page']; + } + else + { + switch ($this->config['current_page']['source']) + { + case 'query_string': + $this->_current_page = isset($_GET[$this->config['current_page']['key']]) + ? (int) $_GET[$this->config['current_page']['key']] + : 1; + break; + + case 'route': + $this->_current_page = (int) Request::current()->param($this->config['current_page']['key'], 1); + break; + } + } + + // Calculate and clean all pagination variables + $this->_total_items = (int) max(0, $this->config['total_items']); + $this->_items_per_page = (int) max(1, $this->config['items_per_page']); + $this->_total_pages = (int) ceil($this->_total_items / $this->_items_per_page); + $this->_current_page = (int) min(max(1, $this->_current_page), max(1, $this->_total_pages)); + $this->_current_first_item = (int) min((($this->_current_page - 1) * $this->_items_per_page) + 1, $this->_total_items); + $this->_current_last_item = (int) min($this->_current_first_item + $this->_items_per_page - 1, $this->_total_items); + $this->_previous_page = ($this->_current_page > 1) ? $this->_current_page - 1 : FALSE; + $this->_next_page = ($this->_current_page < $this->_total_pages) ? $this->_current_page + 1 : FALSE; + $this->_first_page = ($this->_current_page === 1) ? FALSE : 1; + $this->_last_page = ($this->_current_page >= $this->_total_pages) ? FALSE : $this->_total_pages; + $this->_offset = (int) (($this->_current_page - 1) * $this->_items_per_page); + } + + // Chainable method + return $this; + } + + /** + * Generates the full URL for a certain page. + * + * @param integer page number + * @return string page URL + */ + public function url($page = 1) + { + // Clean the page number + $page = max(1, (int) $page); + + // No page number in URLs to first page + if ($page === 1 AND ! $this->config['first_page_in_url']) + { + $page = NULL; + } + + switch ($this->config['current_page']['source']) + { + case 'query_string': + return URL::site(Request::current()->uri()).URL::query(array($this->config['current_page']['key'] => $page)); + + case 'route': + return URL::site(Request::current()->uri(array($this->config['current_page']['key'] => $page))).URL::query(); + } + + return '#'; + } + + /** + * Checks whether the given page number exists. + * + * @param integer page number + * @return boolean + * @since 3.0.7 + */ + public function valid_page($page) + { + // Page number has to be a clean integer + if ( ! Valid::digit($page)) + return FALSE; + + return $page > 0 AND $page <= $this->_total_pages; + } + + /** + * Renders the pagination links. + * + * @param mixed string of the view to use, or a Kohana_View object + * @return string pagination output (HTML) + */ + public function render($view = NULL) + { + // Automatically hide pagination whenever it is superfluous + if ($this->config['auto_hide'] === TRUE AND $this->_total_pages <= 1) + return ''; + + if ($view === NULL) + { + // Use the view from config + $view = $this->config['view']; + } + + if ( ! $view instanceof View) + { + // Load the view file + $view = View::factory($view); + } + + // Pass on the whole Pagination object + return $view->set(get_object_vars($this))->set('page', $this)->render(); + } + + /** + * Renders the pagination links. + * + * @return string pagination output (HTML) + */ + public function __toString() + { + try + { + return $this->render(); + } + catch(Exception $e) + { + Kohana_Exception::handler($e); + return ''; + } + } + + /** + * Handles loading and setting properties. + * + * @param string $method Method name + * @param array $args Method arguments + * @return mixed + */ + public function __call($method, array $args) + { + if (in_array($method, $this->_properties)) + { + if (!count($args)) + { + return $this->{'_'.$method}; + } + } + else + { + throw new Kohana_Exception('Invalid method :method called in :class', + array(':method' => $method, ':class' => get_class($this))); + } + } + + /** + * Handles setting of property + * + * @param string $key Property name + * @param mixed $value Property value + * @return void + */ + public function __set($key, $value) + { + if (isset($this->{'_'.$key})) + { + $this->setup(array($key => $value)); + } + else + { + throw new Kohana_Exception('The :property: property does not exist in the :class: class', + array(':property:' => $key, ':class:' => get_class($this))); + } + } + +} // End Pagination \ No newline at end of file diff --git a/includes/kohana/modules/pagination/classes/pagination.php b/includes/kohana/modules/pagination/classes/pagination.php new file mode 100644 index 00000000..6be4b159 --- /dev/null +++ b/includes/kohana/modules/pagination/classes/pagination.php @@ -0,0 +1,3 @@ + array( + 'current_page' => array('source' => 'query_string', 'key' => 'page'), // source: "query_string" or "route" + 'total_items' => 0, + 'items_per_page' => 10, + 'view' => 'pagination/basic', + 'auto_hide' => TRUE, + 'first_page_in_url' => FALSE, + ), + +); diff --git a/includes/kohana/modules/pagination/config/userguide.php b/includes/kohana/modules/pagination/config/userguide.php new file mode 100644 index 00000000..936b209b --- /dev/null +++ b/includes/kohana/modules/pagination/config/userguide.php @@ -0,0 +1,23 @@ + array( + + // This should be the path to this modules userguide pages, without the 'guide/'. Ex: '/guide/modulename/' would be 'modulename' + 'pagination' => array( + + // Whether this modules userguide pages should be shown + 'enabled' => TRUE, + + // The name that should show up on the userguide index page + 'name' => 'Pagination', + + // A short description of this module, shown on the index page + 'description' => 'Tool for creating paginated links and viewing pages of results.', + + // Copyright message, shown in the footer for this module + 'copyright' => '© 2008–2010 Kohana Team', + ) + ) +); \ No newline at end of file diff --git a/includes/kohana/modules/pagination/guide/pagination/config.md b/includes/kohana/modules/pagination/guide/pagination/config.md new file mode 100644 index 00000000..4a5713bf --- /dev/null +++ b/includes/kohana/modules/pagination/guide/pagination/config.md @@ -0,0 +1,94 @@ +# Pagination Configuration + +[Pagination] uses 6 settings: `current_page`, `total_items`, `items_per_page`, `view`, `auto_hide` and `first_page_in_url`. + +## Configuration Examples + +This example shows the default configuration: + + return array( + + // Application defaults + 'default' => array( + 'current_page' => array('source' => 'query_string', 'key' => 'page'), // source: "query_string" or "route" + 'total_items' => 0, + 'items_per_page' => 10, + 'view' => 'pagination/basic', + 'auto_hide' => TRUE, + 'first_page_in_url' => FALSE, + ), + ); + +This is an example with multiple configurations: + + return array( + + // Application defaults + 'default' => array( + 'current_page' => array('source' => 'query_string', 'key' => 'page'), + 'total_items' => 0, + 'items_per_page' => 10, + 'view' => 'pagination/basic', + 'auto_hide' => TRUE, + 'first_page_in_url' => FALSE, + ), + + // Second configuration + 'pretty' => array( + 'current_page' => array('source' => 'route', 'key' => 'page'), + 'total_items' => 0, + 'items_per_page' => 20, + 'view' => 'pagination/pretty', + 'auto_hide' => TRUE, + 'first_page_in_url' => FALSE, + ), + ); + + + +## Settings + +### current_page + +The `current_page` setting tells Pagination where to look to find the current page number. +There are two options for the `source` of the page number: `query_string` and `route`. +The `key` index in the configuration array tells Pagination what name to look for when it's searching in the query string or route. + +This configuration informs Pagination to look in the query string for a value named `page`: + + 'current_page' => array('source' => 'query_string', 'key' => 'page'), + +If you have a route setup with the page number in the actual URL like this: + + Route::set('city_listings', 'listings(/)', array('page_num' => '[0-9]+')) + ->defaults(array( + 'controller' => 'city', + 'action' => 'listings' + )); + +then you would use a setting like this: + + 'current_page' => array('source' => 'route', 'key' => 'page_num'), + + +### total_items + +`total_items` is a setting you will most likely pass in during runtime after figuring out exactly how many items you have. It can be set to zero in the configuration for now. + +### items_per_page + +Self explanatory. This is the maximum items to show on each page. Pagination determines the total number of pages based off of this number. + +### view + +The `view` setting should be a path to a Pagination view file. + +### auto_hide + +If `auto_hide` is set to `TRUE` then Pagination will automatically hide whenever there's only one page of items. + +### first_page_in_url + +If you want Pagination to add the page number to the first page's link then set this setting to `TRUE` otherwise leave it as `FALSE`. + + diff --git a/includes/kohana/modules/pagination/guide/pagination/examples.md b/includes/kohana/modules/pagination/guide/pagination/examples.md new file mode 100644 index 00000000..e69de29b diff --git a/includes/kohana/modules/pagination/guide/pagination/index.md b/includes/kohana/modules/pagination/guide/pagination/index.md new file mode 100644 index 00000000..e69de29b diff --git a/includes/kohana/modules/pagination/guide/pagination/menu.md b/includes/kohana/modules/pagination/guide/pagination/menu.md new file mode 100644 index 00000000..08c39c8b --- /dev/null +++ b/includes/kohana/modules/pagination/guide/pagination/menu.md @@ -0,0 +1,4 @@ +## [Pagination]() +- [Config](config) +- [Usage](usage) +- [Examples](examples) \ No newline at end of file diff --git a/includes/kohana/modules/pagination/guide/pagination/usage.md b/includes/kohana/modules/pagination/guide/pagination/usage.md new file mode 100644 index 00000000..e69de29b diff --git a/includes/kohana/modules/pagination/views/pagination/basic.php b/includes/kohana/modules/pagination/views/pagination/basic.php new file mode 100644 index 00000000..f0d414d7 --- /dev/null +++ b/includes/kohana/modules/pagination/views/pagination/basic.php @@ -0,0 +1,37 @@ +

+ + first_page() !== FALSE): ?> + + + + + + previous_page() !== FALSE): ?> + + + + + + total_pages(); $i++): ?> + + current_page()): ?> + + + + + + + + next_page() !== FALSE): ?> + + + + + + last_page() !== FALSE): ?> + + + + + +

diff --git a/includes/kohana/modules/pagination/views/pagination/floating.php b/includes/kohana/modules/pagination/views/pagination/floating.php new file mode 100644 index 00000000..52e3fc2b --- /dev/null +++ b/includes/kohana/modules/pagination/views/pagination/floating.php @@ -0,0 +1,94 @@ +total_pages()); + +// Ending group of pages: $n7...$n8 +$n7 = max(1, $page->total_pages() - $count_out + 1); +$n8 = $page->total_pages(); + +// Middle group of pages: $n4...$n5 +$n4 = max($n2 + 1, $page->current_page() - $count_in); +$n5 = min($n7 - 1, $page->current_page() + $count_in); +$use_middle = ($n5 >= $n4); + +// Point $n3 between $n2 and $n4 +$n3 = (int) (($n2 + $n4) / 2); +$use_n3 = ($use_middle && (($n4 - $n2) > 1)); + +// Point $n6 between $n5 and $n7 +$n6 = (int) (($n5 + $n7) / 2); +$use_n6 = ($use_middle && (($n7 - $n5) > 1)); + +// Links to display as array(page => content) +$links = array(); + +// Generate links data in accordance with calculated numbers +for ($i = $n1; $i <= $n2; $i++) +{ + $links[$i] = $i; +} +if ($use_n3) +{ + $links[$n3] = '…'; +} +for ($i = $n4; $i <= $n5; $i++) +{ + $links[$i] = $i; +} +if ($use_n6) +{ + $links[$n6] = '…'; +} +for ($i = $n7; $i <= $n8; $i++) +{ + $links[$i] = $i; +} + +?> +

+ + first_page() !== FALSE): ?> + + + + + + previous_page() !== FALSE): ?> + + + + + + $content): ?> + + current_page()): ?> + + + + + + + + next_page() !== FALSE): ?> + + + + + + last_page() !== FALSE): ?> + + + + + +