Root/
<?php /* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* # ***** BEGIN LICENSE BLOCK ***** # This file is part of Plume Framework, a simple PHP Application Framework. # Copyright (C) 2001-2007 Loic d'Anterroches and contributors. # # Plume Framework is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. # # Plume Framework 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 Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # # ***** END LICENSE BLOCK ***** */ Pluf::loadFunction( 'Pluf_HTTP_URL_urlForView' ); /** * Paginator to display a list of model items. * * The paginator has several methods to render the complete table in * one call or to give more freedom to the end user in the table by * doing it item by item. * * An example of usage is as follow: * * <code> * $model = new Pluf_Permission(); * $pag = new Pluf_Paginator($model); * // Set the action to the page listing the permissions * $pag->action = 'view_name'; * // Get the paginator parameters from the request * $pag->setFromRequest($request); * print $pag->render(); * </code> * * This example shows the fast way. That means that the items will be * shown according to the default values of the paginator or with the * details given in the admin definition of the model. */ class Pluf_Paginator { /** * The model being paginated. */ protected $model ; /** * The items being paginated. If no model is given when creating * the paginator, it will use the items to list them. */ public $items = null; /** * Extra property/value for the items. * * This can be practical if you want some values for the edit * action which are not available in the model data. */ public $item_extra_props = array (); /** * The fields being shown. * * If no fields are given, the __toString representation of the * item will be used to show the item. * * If an item in the list_display is an array the format is the * following: * array('field', 'Custom_Function_ToApply', 'custom header name') * * The signature of the the Custom_Function_ToApply is: * string = Custom_Function_ToApply('field', $item); * * By using for example 'id' as field with a custom header name * you can create new columns in the table. */ protected $list_display = array (); /** * List filter. * * Allow the generation of filtering options for the list. If you * provide a list of fields having a "choices" option, you will be * able to filter on the choice values. */ public $list_filters = array (); /** * Extra classes that will be applied to the td of each cell of * each column. * * If you have 3 columns and put array('one', '', 'foo') all the * td of the first column will have the class 'one' set and the * tds of the last column will have the 'foo' class set. */ public $extra_classes = array (); /** * The fields being searched. */ protected $search_fields = array (); /** * The where clause from the search. */ protected $where_clause = null; /** * The forced where clause on top of the search. */ public $forced_where = null; /** * View of the model to be used. */ public $model_view = null; /** * Maximum number of items per page. */ public $items_per_page = 50; /** * Current page. */ public $current_page = 1; /** * Number of pages. */ public $page_number = 1; /** * Search string. */ public $search_string = '' ; /** * Text to display when no results are found. */ public $no_results_text = 'No items found' ; /** * Which fields of the model can be used to sort the dataset. To * be useable these fields must be in the $list_display so that * the sort links can be shown for the user to click on them and * sort the list. */ public $sort_fields = array (); /** * Current sort order. An array with first value the field and * second the order of the sort. */ public $sort_order = array (); /** * Keys where the sort is reversed. Let say, you have a column * using a timestamp but displaying the information as an age. If * you sort "ASC" you espect to get the youngest first, but as the * timestamp is used, you get the oldest. But the key here and the * sort will be reverted. */ public $sort_reverse_order = array (); /** * * Do not add the little sort links but directly make the title of * the column a link to sort. */ public $sort_link_title = false; /** * Edit action. * */ public $edit_action = '' ; /** * Action for search/next/previous. */ public $action = '' ; /** * Id/class of the generated table. */ public $id = '' ; public $class = '' ; /** * Extra parameters for the modification function call. */ public $extra = null; /** * Summary for the table. */ public $summary = '' ; /** * Total number of items. * * Available only after the rendering of the paginator. */ public $nb_items = 0; protected $active_list_filter = array (); /** * Maximum number of pages to be displayed * * Instead of showing by default unlimited number of pages, * limit to this value. * 0 is unlimited (default). * * Ex: max_number_pages = 3 will produce * Prev 1 ... 498 499 500 ... 1678 Next */ public $max_number_pages = 0; public $max_number_pages_separator = '...' ; public $custom_max_items = false; /** * First, Previous, Next and Last page display * Default First = 1, Last = last page num * Prev and Next are initialized to null. In the footer() we will * set Prev = __('Prev') and Next = __('Next') if not set * Last has to be set during render if not set so that we know * the number of pages */ public $symbol_first = '1' ; public $symbol_last = null; public $symbol_prev = null; public $symbol_next = null; /** * Construct the paginator for a model. * * @param object Model to paginate (null). * @param array List of the headers to show (array()). * @param array List of the fields to search (array()). */ function __construct( $model =null, $list_display = array (), $search_fields = array ()) { $this ->model = $model ; $this ->configure( $list_display , $search_fields ); } /** * Configure the paginator. * * @param array List of the headers to show. * @param array List of the fields to search (array()) * @param array List of the fields to sort the data set (array()) */ function configure( $list_display , $search_fields = array (), $sort_fields = array ()) { if ( is_array ( $list_display )) { $this ->list_display = array (); foreach ( $list_display as $key => $col ) { if (! is_array ( $col ) && ! is_null ( $this ->model) && isset( $this ->model->_a[ 'cols' ][ $col ][ 'verbose' ])) { $this ->list_display[ $col ] = $this ->model->_a[ 'cols' ][ $col ][ 'verbose' ]; } elseif (! is_array ( $col )) { if ( is_numeric ( $key )) { $this ->list_display[ $col ] = $col ; } else { $this ->list_display[ $key ] = $col ; } } else { if ( count ( $col ) == 2 && ! is_null ( $this ->model) && isset( $this ->model->_a[ 'cols' ][ $col [0]][ 'verbose' ])) { $col [2] = $this ->model->_a[ 'cols' ][ $col [0]][ 'verbose' ]; } elseif ( count ( $col ) == 2 ) { $col [2] = $col [0]; } $this ->list_display[] = $col ; } } } //print_r($this->list_display); if ( is_array ( $search_fields )) { $this ->search_fields = $search_fields ; } if ( is_array ( $sort_fields )) { $this ->sort_fields = $sort_fields ; } } /** * Set the parameters from the request. * * Possible parameters are: * _px_q : Query string to search. * _px_p : Current page. * _px_sk : Sort key. * _px_so : Sort order. * _px_fk : Filter key. * _px_fv : Filter value. * * @param Pluf_HTTP_Request The request */ function setFromRequest( $request ) { if (isset( $request ->REQUEST[ '_px_q' ])) { $this ->search_string = $request ->REQUEST[ '_px_q' ]; } if (isset( $request ->REQUEST[ '_px_p' ])) { $this ->current_page = (int) $request ->REQUEST[ '_px_p' ]; $this ->current_page = max(1, $this ->current_page); } if (isset( $request ->REQUEST[ '_px_sk' ]) and in_array( $request ->REQUEST[ '_px_sk' ], $this ->sort_fields)) { $this ->sort_order[0] = $request ->REQUEST[ '_px_sk' ]; $this ->sort_order[1] = 'ASC' ; if (isset( $request ->REQUEST[ '_px_so' ]) and ( $request ->REQUEST[ '_px_so' ] == 'd' )) { $this ->sort_order[1] = 'DESC' ; } } if (isset( $request ->REQUEST[ '_px_fk' ]) and in_array( $request ->REQUEST[ '_px_fk' ], $this ->list_filters) and isset( $request ->REQUEST[ '_px_fv' ])) { // We add a forced where query $sql = new Pluf_SQL( $request ->REQUEST[ '_px_fk' ]. '=%s' , $request ->REQUEST[ '_px_fv' ]); if (! is_null ( $this ->forced_where)) { $this ->forced_where->SAnd( $sql ); } else { $this ->forced_where = $sql ; } $this ->active_list_filter = array ( $request ->REQUEST[ '_px_fk' ], $request ->REQUEST[ '_px_fv' ]); } } /** * Render the complete table. * * When an id is provided, the generated table receive this id. * * @param string Table id ('') */ function render( $id = '' ) { $this ->id = $id ; $_sum = '' ; if ( strlen ( $this ->summary)) { $_sum = ' summary="' .htmlspecialchars( $this ->summary). '"' ; } $out = '<table' . $_sum .(( $this -> class ) ? ' class="' . $this -> class . '"' : '' ).(( $this ->id) ? ' id="' . $this ->id. '">' : '>' ). "\n" ; $out .= '<thead>' . "\n" ; $out .= $this ->searchField(); $out .= $this ->colHeaders(); $out .= '</thead>' . "\n" ; // Opt: Generate the footer of the table with the next/previous links $out .= $this ->footer(); // Generate the body of the table with the items $out .= $this ->body(); $out .= '</table>' . "\n" ; return new Pluf_Template_SafeString( $out , true); } /** * Render as array. * * An array rendering do not honor the limits, that is, all the * items are returned. Also, the output is not formatted values * from the db are directly returned. This is perfect to then use * the values in a JSON response. * * @return Array. */ function render_array() { if ( count ( $this ->sort_order) != 2) { $order = null; } else { $s = $this ->sort_order[1]; if (in_array( $this ->sort_order[0], $this ->sort_reverse_order)) { $s = ( $s == 'ASC' ) ? 'DESC' : 'ASC' ; } $order = $this ->sort_order[0]. ' ' . $s ; } if (! is_null ( $this ->model)) { $items = $this ->model->getList( array ( 'view' => $this ->model_view, 'filter' => $this ->filter(), 'order' => $order , )); } else { $items = $this ->items; } $out = array (); foreach ( $items as $item ) { $idata = array (); if (! empty ( $this ->list_display)) { $i = 0; foreach ( $this ->list_display as $key => $col ) { if (! is_array ( $col )) { $idata [ $key ] = $item -> $key ; } else { $_col = $col [0]; $idata [ $col [0]] = $item -> $_col ; } } } else { $idata = $item ->id; } $out [] = $idata ; } return $out ; } /** * Generate the footer of the table. */ function footer() { // depending on the search string, the result set can be // limited. So we need first to count the total number of // corresponding items. Then get a slice of them in the // generation of the body. if (! is_null ( $this ->model)) { $nb_items = $this ->model->getCount( array ( 'view' => $this ->model_view, 'filter' => $this ->filter())); } else { $nb_items = $this ->items-> count (); } $this ->nb_items = $nb_items ; if ( $nb_items <= $this ->items_per_page) { return '' ; } $this ->page_number = ceil ( $nb_items / $this ->items_per_page); if ( $this ->current_page > $this ->page_number) { $this ->current_page = 1; } $params = array (); if (! empty ( $this ->search_fields)) { $params [ '_px_q' ] = $this ->search_string; $params [ '_px_p' ] = $this ->current_page; } if (! empty ( $this ->sort_order)) { $params [ '_px_sk' ] = $this ->sort_order[0]; $params [ '_px_so' ] = ( $this ->sort_order[1] == 'ASC' ) ? 'a' : 'd' ; } // Add the filtering if (! empty ( $this ->active_list_filter)) { $params [ '_px_fk' ] = $this ->active_list_filter[0]; $params [ '_px_fv' ] = $this ->active_list_filter[1]; } $out = '<tfoot><tr><th colspan="' . count ( $this ->list_display). '">' . "\n" ; if ( $this ->current_page != 1) { $params [ '_px_p' ] = $this ->current_page - 1; $url = $this ->getUrl( $params ); $this ->symbol_prev = ( $this ->symbol_prev ==null) ?__( 'Prev' ) : $this ->symbol_prev; $out .= '<a href="' . $url . '">' . $this ->symbol_prev. '</a> ' ; } // Always display the link to Page#1 $i =1; $params [ '_px_p' ] = $i ; $class = ( $i == $this ->current_page) ? ' class="px-current-page"' : '' ; $url = $this ->getUrl( $params ); $out .= '<a' . $class . ' href="' . $url . '">' . $this ->symbol_first. '</a> ' ; // Display the number of pages given $this->max_number_pages if ( $this ->max_number_pages > 0) { $nb_pa = floor ( $this ->max_number_pages/2); $imin = $this ->current_page - $nb_pa ; $imax = $this ->current_page + $nb_pa ; // We put the separator if $imin is at leat greater than 2 if ( $imin > 2) $out .= ' ' . $this ->max_number_pages_separator. ' ' ; if ( $imin <= 1) $imin =2; if ( $imax >= $this ->page_number) $imax = $this ->page_number - 1; } else { $imin = 2; $imax = $this ->page_number - 1; } for ( $i = $imin ; $i <= $imax ; $i ++) { $params [ '_px_p' ] = $i ; $class = ( $i == $this ->current_page) ? ' class="px-current-page"' : '' ; $url = $this ->getUrl( $params ); $out .= '<a' . $class . ' href="' . $url . '">' . $i . '</a> ' ; } if (( $this ->max_number_pages > 0) && $imax < ( $this ->page_number - 1)) { $out .= ' ' . $this ->max_number_pages_separator. ' ' ; } // Always display the link to last Page $i = $this ->page_number; $params [ '_px_p' ] = $i ; $class = ( $i == $this ->current_page) ? ' class="px-current-page"' : '' ; $url = $this ->getUrl( $params ); if ( $this ->symbol_last == null) $this ->symbol_last= $i ; $out .= '<a' . $class . ' href="' . $url . '">' . $this ->symbol_last. '</a> ' ; if ( $this ->current_page != $this ->page_number) { $params [ '_px_p' ] = $this ->current_page + 1; $url = $this ->getUrl( $params ); $this ->symbol_next = ( $this ->symbol_next == null) ? __( 'Next' ) : $this ->symbol_next; $out .= '<a href="' . $url . '">' . $this ->symbol_next. '</a> ' ; } $out .= '</th></tr></tfoot>' . "\n" ; return $out ; } /** * Generate the body of the list. */ function body() { $st = ( $this ->current_page-1) * $this ->items_per_page; if ( count ( $this ->sort_order) != 2) { $order = null; } else { $s = $this ->sort_order[1]; if (in_array( $this ->sort_order[0], $this ->sort_reverse_order)) { $s = ( $s == 'ASC' ) ? 'DESC' : 'ASC' ; } $order = $this ->sort_order[0]. ' ' . $s ; } if (! is_null ( $this ->model)) { $items = $this ->model->getList( array ( 'view' => $this ->model_view, 'filter' => $this ->filter(), 'order' => $order , 'start' => $st , 'nb' => $this ->items_per_page)); } else { $items = $this ->items; } $out = '' ; $total = $items -> count (); $count = 1; foreach ( $items as $item ) { $item ->_paginator_count = $count ; $item ->_paginator_total_page = $total ; foreach ( $this ->item_extra_props as $key => $val ) { $item -> $key = $val ; } $out .= $this ->bodyLine( $item ); $count ++; } if ( strlen ( $out ) == 0) { $out = '<tr><td colspan="' . count ( $this ->list_display). '">' . $this ->no_results_text . '</td></tr>' . "\n" ; } return '<tbody>' . $out . '</tbody>' . "\n" ; } /** * Generate a standard "line" of the body */ function bodyLine( $item ) { $out = '<tr>' ; if (! empty ( $this ->list_display)) { $i = 0; foreach ( $this ->list_display as $key => $col ) { $text = '' ; if (! is_array ( $col )) { $text = Pluf_esc( $item -> $key ); } else { if ( is_null ( $this ->extra)) { $text = $col [1]( $col [0], $item ); } else { $text = $col [1]( $col [0], $item , $this ->extra); } } if ( $i == 0) { $text = $this ->getEditAction( $text , $item ); } $class = (isset( $this ->extra_classes[ $i ]) and $this ->extra_classes[ $i ] != '' ) ? ' class="' . $this ->extra_classes[ $i ]. '"' : '' ; $out .= '<td' . $class . '>' . $text . '</td>' ; $i ++; } } else { $out .= '<td>' . $this ->getEditAction(Pluf_esc( $item ), $item ). '</td>' ; } $out .= '</tr>' . "\n" ; return $out ; } /** * Get the edit action. * * @param string Text to put in the action. * No escaping of the text is performed. * @param object Model for the action. * @return string Ready to use string. */ function getEditAction( $text , $item ) { $edit_action = $this ->edit_action; if (! empty ( $edit_action )) { if (! is_array ( $edit_action )) { $params = array ( $edit_action , $item ->id); } else { $params = array ( array_shift ( $edit_action )); foreach ( $edit_action as $field ) { $params [] = $item -> $field ; } } $view = array_shift ( $params ); $url = Pluf_HTTP_URL_urlForView( $view , $params ); return '<a href="' . $url . '">' . $text . '</a>' ; } else { return $text ; } } /** * Generate the where clause. * * @return string The ready to use where clause. */ function filter() { if ( strlen ( $this ->where_clause) > 0) { return $this ->where_clause; } if (! is_null ( $this ->forced_where) or ( strlen ( $this ->search_string) > 0 && ! empty ( $this ->search_fields))) { $lastsql = new Pluf_SQL(); $keywords = $lastsql ->keywords( $this ->search_string); foreach ( $keywords as $key ) { $sql = new Pluf_SQL(); foreach ( $this ->search_fields as $field ) { $sqlor = new Pluf_SQL(); $sqlor ->Q( $field . ' LIKE %s' , '%' . $key . '%' ); $sql ->SOr( $sqlor ); } $lastsql ->SAnd( $sql ); } if (! is_null ( $this ->forced_where)) { $lastsql ->SAnd( $this ->forced_where); } $this ->where_clause = $lastsql ->gen(); if ( strlen ( $this ->where_clause) == 0) $this ->where_clause = null; } return $this ->where_clause; } /** * Generate the column headers for the table. */ function colHeaders() { if ( empty ( $this ->list_display)) { return '<tr><th>' .__( 'Name' ). '</th></tr>' . "\n" ; } else { $out = '<tr>' ; foreach ( $this ->list_display as $key => $col ) { if ( is_array ( $col )) { $field = $col [0]; $name = $col [2]; Pluf::loadFunction( $col [1]); } else { $name = $col ; $field = $key ; } //print_r($this->list_display); if (! $this ->sort_link_title) { $out .= '<th><span class="px-header-title">' .Pluf_esc(ucfirst( $name )). '</span>' . $this ->headerSortLinks( $field ). '</th>' ; } else { $out .= '<th><span class="px-header-title">' . $this ->headerSortLinks( $field , Pluf_esc(ucfirst( $name ))). '</span></th>' ; } } $out .= '</tr>' . "\n" ; return $out ; } } /** * Generate the little text on the header to allow sorting if * available. * * If the title is set, the link is directly made on the title. * * @param string Name of the field * @param string Title ('') * @return string HTML fragment with the links to * sort ASC/DESC on this field. */ function headerSortLinks( $field , $title = '' ) { if (!in_array( $field , $this ->sort_fields)) { return $title ; } $params = array (); if (! empty ( $this ->search_fields)) { $params [ '_px_q' ] = $this ->search_string; } if (! empty ( $this ->active_list_filter)) { $params [ '_px_fk' ] = $this ->active_list_filter[0]; $params [ '_px_fv' ] = $this ->active_list_filter[1]; } $params [ '_px_sk' ] = $field ; $out = '<span class="px-sort">' .__( 'Sort' ). ' %s/%s</span>' ; $params [ '_px_so' ] = 'a' ; $aurl = $this ->getUrl( $params ); $asc = '<a href="' . $aurl . '" >' .__( 'asc' ). '</a>' ; $params [ '_px_so' ] = 'd' ; $durl = $this ->getUrl( $params ); $desc = '<a href="' . $durl . '" >' .__( 'desc' ). '</a>' ; if ( strlen ( $title )) { if ( count ( $this ->sort_order) == 2 and $this ->sort_order[0] == $field and $this ->sort_order[1] == 'ASC' ) { return '<a href="' . $durl . '" >' . $title . '</a>' ; } return '<a href="' . $aurl . '" >' . $title . '</a>' ; } return sprintf( $out , $asc , $desc ); } /** * Get the search field XHTML. */ function searchField() { if ( empty ( $this ->search_fields)) { return '' ; } $url = $this ->getUrl(); return '<tr><th class="px-table-search" colspan="' . count ( $this ->list_display). '">' . '<form method="get" action="' . $url . '">' . '<label for="px-q">' .__( 'Filter the list:' ). '</label> ' . '<input type="text" name="_px_q" id="px-q" size="30"' . ' value="' .htmlspecialchars( $this ->search_string). '" />' . '<input type="submit" name="submit" value="' .__( 'Filter' ). '" />' . '</form></th></tr>' . "\n" ; } /** * Using $this->action and the $get_params array, generate the URL * with the data. * * @param array Get parameters (array()). * @param bool Encoded to be put in href="" (true). * @return string Url. */ function getUrl( $get_params = array (), $encoded =true) { // Default values $params = array (); $by_name = false; $view = '' ; if ( is_array ( $this ->action)) { $view = $this ->action[0]; if (isset( $this ->action[1])) { $params = $this ->action[1]; } } else { $view = $this ->action; } return Pluf_HTTP_URL_urlForView( $view , $params , $get_params , $encoded ); } /** * Overloading of the get method. * * @param string Property to get */ function __get( $prop ) { if ( $prop == 'render' ) return $this ->render(); return $this -> $prop ; } } /** * Returns the string representation of an item. * * @param string Field (not used) * @param Object Item * @return string Representation of the item */ function Pluf_Paginator_ToString( $field , $item ) { return Pluf_esc( $item ); } /** * Returns the item referenced as foreign key as a string. */ function Pluf_Paginator_FkToString( $field , $item ) { $method = 'get_' . $field ; $fk = $item -> $method (); return Pluf_esc( $fk ); } function Pluf_Paginator_DateYMDHMS( $field , $item ) { Pluf::loadFunction( 'Pluf_Template_dateFormat' ); return Pluf_Template_dateFormat( $item -> $field , '%Y-%m-%d %H:%M:%S' ); } function Pluf_Paginator_DateYMDHM( $field , $item ) { Pluf::loadFunction( 'Pluf_Template_dateFormat' ); return Pluf_Template_dateFormat( $item -> $field , '%Y-%m-%d %H:%M' ); } function Pluf_Paginator_DateYMD( $field , $item ) { Pluf::loadFunction( 'Pluf_Template_dateFormat' ); return Pluf_Template_dateFormat( $item -> $field , '%Y-%m-%d' ); } function Pluf_Paginator_DisplayVal( $field , $item ) { return $item ->displayVal( $field ); } function Pluf_Paginator_DateAgo( $field , $item ) { Pluf::loadFunction( 'Pluf_Date_Easy' ); Pluf::loadFunction( 'Pluf_Template_dateFormat' ); $date = Pluf_Template_dateFormat( $item -> $field , '%Y-%m-%d %H:%M:%S' ); return Pluf_Date_Easy( $date , null, 2, __( 'now' )); } |
Source at commit 7d92c7ed8bd9 created 10 years 9 months ago. By "Nathan Adams ", Adding some extension mapping to FileUtil |
---|